package org.richin.file.util;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.Checksum;
import org.apache.commons.io.LineIterator;
import org.apache.commons.io.filefilter.FalseFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richin.collection.util.CollectionUtils;
import org.richin.io.util.IOUtils;
import org.richin.lang.util.StringUtils;
import org.richin.system.util.Assert;

/**
 * File操作帮助类基于commons.io.FileUtils
 * @author Administrator
 *
 */
public class FileUtils {

	public static final String FILE_SEPARATOR = System.getProperty("file.separator");
	public static final String TEMP_DIR = System.getProperty("java.io.tempdir");
	private final static int KILOBYTE = 1024;
	public static final int BUFFER_SIZE = 4096;
	/**
	* The number of bytes in a kilobyte.
	*/
	public static final long ONE_KB = 1024;

	/**
	 * The number of bytes in a megabyte.
	 */
	public static final long ONE_MB = ONE_KB * ONE_KB;

	/**
	 * The number of bytes in a gigabyte.
	 */
	public static final long ONE_GB = ONE_KB * ONE_MB;

	/**
	 * An empty array of type <code>File</code>.
	 */
	public static final File[] EMPTY_FILE_ARRAY = new File[0];
	private static final Log log = LogFactory.getLog(FileUtils.class);

	public static File createTempFile(String prefix, String suffix) throws IOException {
		Random rnd = new Random();
		prefix = (new StringBuilder()).append(prefix).append(rnd.nextInt()).toString();
		return createTempFile(prefix, suffix, null);
	}


	public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
		Random rnd = new Random();
		prefix = (new StringBuilder()).append(prefix).append(rnd.nextInt()).toString();
		return File.createTempFile(prefix, suffix, directory);
	}

	public static File makeBackup(File file, File backupDirectory, boolean moveAllowed) throws IOException {
		File copy = File.createTempFile(file.getName(), ".backup", backupDirectory);
		boolean success = false;
		if (moveAllowed) {
			success = file.renameTo(copy);
		}
		if (!success) {
			copy(file, copy);
		}
		return copy;
	}

	/**
	 * Opens a {@link FileInputStream} for the specified file, providing better
	 * error messages than simply calling <code>new FileInputStream(file)</code>.
	 * <p>
	 * At the end of the method either the stream will be successfully opened,
	 * or an exception will have been thrown.
	 * <p>
	 * An exception is thrown if the file does not exist.
	 * An exception is thrown if the file object exists but is a directory.
	 * An exception is thrown if the file exists but cannot be read.
	 * 
	 * @param file  the file to open for input, must not be <code>null</code>
	 * @return a new {@link FileInputStream} for the specified file
	 * @throws FileNotFoundException if the file does not exist
	 * @throws IOException if the file object is a directory
	 * @throws IOException if the file cannot be read
	 * @since Commons IO 1.3
	 */
	public static FileInputStream openInputStream(File file) throws IOException {
		if (file.exists()) {
			if (file.isDirectory()) {
				throw new IOException("File '" + file + "' exists but is a directory");
			}
			if (file.canRead() == false) {
				throw new IOException("File '" + file + "' cannot be read");
			}
		} else {
			throw new FileNotFoundException("File '" + file + "' does not exist");
		}
		return new FileInputStream(file);
	}

	//-----------------------------------------------------------------------
	/**
	 * Opens a {@link FileOutputStream} for the specified file, checking and
	 * creating the parent directory if it does not exist.
	 * <p>
	 * At the end of the method either the stream will be successfully opened,
	 * or an exception will have been thrown.
	 * <p>
	 * The parent directory will be created if it does not exist.
	 * The file will be created if it does not exist.
	 * An exception is thrown if the file object exists but is a directory.
	 * An exception is thrown if the file exists but cannot be written to.
	 * An exception is thrown if the parent directory cannot be created.
	 * 
	 * @param file  the file to open for output, must not be <code>null</code>
	 * @return a new {@link FileOutputStream} for the specified file
	 * @throws IOException if the file object is a directory
	 * @throws IOException if the file cannot be written to
	 * @throws IOException if a parent directory needs creating but that fails
	 * @since Commons IO 1.3
	 */
	public static FileOutputStream openOutputStream(File file) throws IOException {
		if (file.exists()) {
			if (file.isDirectory()) {
				throw new IOException("File '" + file + "' exists but is a directory");
			}
			if (file.canWrite() == false) {
				throw new IOException("File '" + file + "' cannot be written to");
			}
		} else {
			File parent = file.getParentFile();
			if (parent != null && parent.exists() == false) {
				if (parent.mkdirs() == false) {
					throw new IOException("File '" + file + "' could not be created");
				}
			}
		}
		return new FileOutputStream(file);
	}

	//-----------------------------------------------------------------------
	/**
	 * Returns a human-readable version of the file size, where the input
	 * represents a specific number of bytes.
	 *
	 * @param size  the number of bytes
	 * @return a human-readable display value (includes units)
	 */
	public static String byteCountToDisplaySize(long size) {
		String displaySize;
		size=Math.abs(size);
		if (size / ONE_GB > 0) {
			displaySize = String.valueOf(size / ONE_GB) + " GB";
		} else if (size / ONE_MB > 0) {
			displaySize = String.valueOf(size / ONE_MB) + " MB";
		} else if (size / ONE_KB > 0) {
			displaySize = String.valueOf(size / ONE_KB) + " KB";
		} else {
			displaySize = String.valueOf(size) + " bytes";
		}
		return displaySize;
	}
	public static double byteToDisplaySize(long size) {
		double displaySize;
		size=Math.abs(size);
		if (size / ONE_GB > 0) {
			displaySize = (size / ONE_GB);
		} else if (size / ONE_MB > 0) {
			displaySize = (size / ONE_MB);
		} else if (size / ONE_KB > 0) {
			displaySize = (size / ONE_KB);
		} else {
			displaySize = size;
		}
		return displaySize;
	}

	//-----------------------------------------------------------------------
	/**
	 * Implements the same behaviour as the "touch" utility on Unix. It creates
	 * a new file with size 0 or, if the file exists already, it is opened and
	 * closed without modifying it, but updating the file date and time.
	 * <p>
	 * NOTE: As from v1.3, this method throws an IOException if the last
	 * modified date of the file cannot be set. Also, as from v1.3 this method
	 * creates parent directories if they do not exist.
	 *
	 * @param file  the File to touch
	 * @throws IOException If an I/O problem occurs
	 */
	public static void touch(File file) throws IOException {
		if (!file.exists()) {
			OutputStream out = openOutputStream(file);
			IOUtils.closeQuietly(out);
		}
		boolean success = file.setLastModified(System.currentTimeMillis());
		if (!success) {
			throw new IOException("Unable to set the last modification time for " + file);
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Converts a Collection containing java.io.File instanced into array
	 * representation. This is to account for the difference between
	 * File.listFiles() and FileUtils.listFiles().
	 *
	 * @param files  a Collection containing java.io.File instances
	 * @return an array of java.io.File
	 */
	public static File[] convertFileCollectionToFileArray(Collection files) {
		return (File[]) files.toArray(new File[files.size()]);
	}

	/**
	 * Allows iteration over the files in given directory (and optionally
	 * its subdirectories).
	 * <p>
	 * All files found are filtered by an IOFileFilter. This method is
	 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
	 *
	 * @param directory  the directory to search in
	 * @param fileFilter  filter to apply when finding files.
	 * @param dirFilter  optional filter to apply when finding subdirectories.
	 * If this parameter is <code>null</code>, subdirectories will not be included in the
	 * search. Use TrueFileFilter.INSTANCE to match all directories.
	 * @return an iterator of java.io.File for the matching files
	 * @see org.apache.commons.io.filefilter.FileFilterUtils
	 * @see org.apache.commons.io.filefilter.NameFileFilter
	 * @since Commons IO 1.2
	 */
	public static Iterator<File> iterateFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
		return listFiles(directory, fileFilter, dirFilter).iterator();
	}

	//-----------------------------------------------------------------------
	/**
	 * Converts an array of file extensions to suffixes for use
	 * with IOFileFilters.
	 *
	 * @param extensions  an array of extensions. Format: {"java", "xml"}
	 * @return an array of suffixes. Format: {".java", ".xml"}
	 */
	private static String[] toSuffixes(String[] extensions) {
		String[] suffixes = new String[extensions.length];
		for (int i = 0; i < extensions.length; i++) {
			suffixes[i] = "." + extensions[i];
		}
		return suffixes;
	}

	/**
	 * Finds files within a given directory (and optionally its subdirectories)
	 * which match an array of extensions.
	 *
	 * @param directory  the directory to search in
	 * @param extensions  an array of extensions, ex. {"java","xml"}. If this
	 * parameter is <code>null</code>, all files are returned.
	 * @param recursive  if true all subdirectories are searched as well
	 * @return an collection of java.io.File with the matching files
	 */
	public static Collection listFiles1(File directory, String[] extensions, boolean recursive) {
		IOFileFilter filter;
		if (extensions == null) {
			filter = TrueFileFilter.INSTANCE;
		} else {
			String[] suffixes = toSuffixes(extensions);
			filter = new SuffixFileFilter(suffixes);
		}
		return listFiles(directory, filter, (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
	}

	/**
	 * Convert from a <code>URL</code> to a <code>File</code>.
	 * <p>
	 * From version 1.1 this method will decode the URL.
	 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
	 * correctly decoded to <code>/my docs/file.txt</code>.
	 *
	 * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
	 * @return the equivalent <code>File</code> object, or <code>null</code>
	 *  if the URL's protocol is not <code>file</code>
	 * @throws IllegalArgumentException if the file is incorrectly encoded
	 */
	public static File toFile1(URL url) {
		if (url == null || !url.getProtocol().equals("file")) {
			return null;
		} else {
			String filename = url.getFile().replace('/', File.separatorChar);
			int pos = 0;
			while ((pos = filename.indexOf('%', pos)) >= 0) {
				if (pos + 2 < filename.length()) {
					String hexStr = filename.substring(pos + 1, pos + 3);
					char ch = (char) Integer.parseInt(hexStr, 16);
					filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
				}
			}
			return new File(filename);
		}
	}

	/**
	 * Converts each of an array of <code>URL</code> to a <code>File</code>.
	 * <p>
	 * Returns an array of the same size as the input.
	 * If the input is <code>null</code>, an empty array is returned.
	 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
	 * index.
	 * <p>
	 * This method will decode the URL.
	 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
	 * correctly decoded to <code>/my docs/file.txt</code>.
	 *
	 * @param urls  the file URLs to convert, <code>null</code> returns empty array
	 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
	 *  if there was a <code>null</code> at that index in the input array
	 * @throws IllegalArgumentException if any file is not a URL file
	 * @throws IllegalArgumentException if any file is incorrectly encoded
	 * @since Commons IO 1.1
	 */
	public static File[] toFiles(URL[] urls) {
		if (urls == null || urls.length == 0) {
			return EMPTY_FILE_ARRAY;
		}
		File[] files = new File[urls.length];
		for (int i = 0; i < urls.length; i++) {
			URL url = urls[i];
			if (url != null) {
				if (url.getProtocol().equals("file") == false) {
					throw new IllegalArgumentException("URL could not be converted to a File: " + url);
				}
				files[i] = toFile1(url);
			}
		}
		return files;
	}

	/**
	 * Converts each of an array of <code>File</code> to a <code>URL</code>.
	 * <p>
	 * Returns an array of the same size as the input.
	 *
	 * @param files  the files to convert
	 * @return an array of URLs matching the input
	 * @throws IOException if a file cannot be converted
	 */
	public static URL[] toURLs(File[] files) throws IOException {
		URL[] urls = new URL[files.length];

		for (int i = 0; i < urls.length; i++) {
			urls[i] = files[i].toURL();
		}

		return urls;
	}

	/**
	 * Allows iteration over the files in a given directory (and optionally
	 * its subdirectories) which match an array of extensions. This method
	 * is based on {@link #listFiles(File, String[], boolean)}.
	 *
	 * @param directory  the directory to search in
	 * @param extensions  an array of extensions, ex. {"java","xml"}. If this
	 * parameter is <code>null</code>, all files are returned.
	 * @param recursive  if true all subdirectories are searched as well
	 * @return an iterator of java.io.File with the matching files
	 * @since Commons IO 1.2
	 */
	public static Iterator iterateFiles(File directory, String[] extensions, boolean recursive) {
		return listFiles(directory, extensions, recursive).iterator();
	}

	//-----------------------------------------------------------------------
	/**
	 * Copies a file to a directory preserving the file date.
	 * <p>
	 * This method copies the contents of the specified source file
	 * to a file of the same name in the specified destination directory.
	 * The destination directory is created if it does not exist.
	 * If the destination file exists, then this method will overwrite it.
	 *
	 * @param srcFile  an existing file to copy, must not be <code>null</code>
	 * @param destDir  the directory to place the copy in, must not be <code>null</code>
	 *
	 * @throws NullPointerException if source or destination is null
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @see #copyFile(File, File, boolean)
	 */
	public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
		copyFileToDirectory(srcFile, destDir, true);
	}

	/**
	 * Copies a file to a directory optionally preserving the file date.
	 * <p>
	 * This method copies the contents of the specified source file
	 * to a file of the same name in the specified destination directory.
	 * The destination directory is created if it does not exist.
	 * If the destination file exists, then this method will overwrite it.
	 *
	 * @param srcFile  an existing file to copy, must not be <code>null</code>
	 * @param destDir  the directory to place the copy in, must not be <code>null</code>
	 * @param preserveFileDate  true if the file date of the copy
	 *  should be the same as the original
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @see #copyFile(File, File, boolean)
	 * @since Commons IO 1.3
	 */
	public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
		if (destDir == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (destDir.exists() && destDir.isDirectory() == false) {
			throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
		}
		copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
	}

	/**
	 * Copies a file to a new location preserving the file date.
	 * <p>
	 * This method copies the contents of the specified source file to the
	 * specified destination file. The directory holding the destination file is
	 * created if it does not exist. If the destination file exists, then this
	 * method will overwrite it.
	 * 
	 * @param srcFile  an existing file to copy, must not be <code>null</code>
	 * @param destFile  the new file, must not be <code>null</code>
	 * 
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @see #copyFileToDirectory(File, File)
	 */
	public static void copyFile(File srcFile, File destFile) throws IOException {
		copyFile(srcFile, destFile, true);
	}

	/**
	 * Copies a file to a new location.
	 * <p>
	 * This method copies the contents of the specified source file
	 * to the specified destination file.
	 * The directory holding the destination file is created if it does not exist.
	 * If the destination file exists, then this method will overwrite it.
	 *
	 * @param srcFile  an existing file to copy, must not be <code>null</code>
	 * @param destFile  the new file, must not be <code>null</code>
	 * @param preserveFileDate  true if the file date of the copy
	 *  should be the same as the original
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @see #copyFileToDirectory(File, File, boolean)
	 */
	public static void copyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
		if (srcFile == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destFile == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (srcFile.exists() == false) {
			throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
		}
		if (srcFile.isDirectory()) {
			throw new IOException("Source '" + srcFile + "' exists but is a directory");
		}
		if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
			throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
		}
		if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
			if (destFile.getParentFile().mkdirs() == false) {
				throw new IOException("Destination '" + destFile + "' directory cannot be created");
			}
		}
		if (destFile.exists() && destFile.canWrite() == false) {
			throw new IOException("Destination '" + destFile + "' exists but is read-only");
		}
		doCopyFile(srcFile, destFile, preserveFileDate);
	}

	/**
	 * Internal copy file method.
	 * 
	 * @param srcFile  the validated source file, must not be <code>null</code>
	 * @param destFile  the validated destination file, must not be <code>null</code>
	 * @param preserveFileDate  whether to preserve the file date
	 * @throws IOException if an error occurs
	 */
	private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
		if (destFile.exists() && destFile.isDirectory()) {
			throw new IOException("Destination '" + destFile + "' exists but is a directory");
		}

		FileInputStream input = new FileInputStream(srcFile);
		try {
			FileOutputStream output = new FileOutputStream(destFile);
			try {
				IOUtils.copy(input, output);
			} finally {
				IOUtils.closeQuietly(output);
			}
		} finally {
			IOUtils.closeQuietly(input);
		}

		if (srcFile.length() != destFile.length()) {
			throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'");
		}
		if (preserveFileDate) {
			destFile.setLastModified(srcFile.lastModified());
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Copies a directory to within another directory preserving the file dates.
	 * <p>
	 * This method copies the source directory and all its contents to a
	 * directory of the same name in the specified destination directory.
	 * <p>
	 * The destination directory is created if it does not exist.
	 * If the destination directory did exist, then this method merges
	 * the source with the destination, with the source taking precedence.
	 *
	 * @param srcDir  an existing directory to copy, must not be <code>null</code>
	 * @param destDir  the directory to place the copy in, must not be <code>null</code>
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @since Commons IO 1.2
	 */
	public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
		if (srcDir == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (srcDir.exists() && srcDir.isDirectory() == false) {
			throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (destDir.exists() && destDir.isDirectory() == false) {
			throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
		}
		copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
	}

	/**
	 * Copies a whole directory to a new location preserving the file dates.
	 * <p>
	 * This method copies the specified directory and all its child
	 * directories and files to the specified destination.
	 * The destination is the new location and name of the directory.
	 * <p>
	 * The destination directory is created if it does not exist.
	 * If the destination directory did exist, then this method merges
	 * the source with the destination, with the source taking precedence.
	 *
	 * @param srcDir  an existing directory to copy, must not be <code>null</code>
	 * @param destDir  the new directory, must not be <code>null</code>
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @since Commons IO 1.1
	 */
	public static void copyDirectory(File srcDir, File destDir) throws IOException {
		copyDirectory(srcDir, destDir, true);
	}

	/**
	 * Copies a whole directory to a new location.
	 * <p>
	 * This method copies the contents of the specified source directory
	 * to within the specified destination directory.
	 * <p>
	 * The destination directory is created if it does not exist.
	 * If the destination directory did exist, then this method merges
	 * the source with the destination, with the source taking precedence.
	 *
	 * @param srcDir  an existing directory to copy, must not be <code>null</code>
	 * @param destDir  the new directory, must not be <code>null</code>
	 * @param preserveFileDate  true if the file date of the copy
	 *  should be the same as the original
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @since Commons IO 1.1
	 */
	public static void copyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException {
		copyDirectory(srcDir, destDir, null, preserveFileDate);
	}

	/**
	 * Copies a filtered directory to a new location preserving the file dates.
	 * <p>
	 * This method copies the contents of the specified source directory
	 * to within the specified destination directory.
	 * <p>
	 * The destination directory is created if it does not exist.
	 * If the destination directory did exist, then this method merges
	 * the source with the destination, with the source taking precedence.
	 *
	 * <h4>Example: Copy directories only</h4> 
	 *  <pre>
	 *  // only copy the directory structure
	 *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
	 *  </pre>
	 *
	 * <h4>Example: Copy directories and txt files</h4>
	 *  <pre>
	 *  // Create a filter for ".txt" files
	 *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
	 *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
	 *
	 *  // Create a filter for either directories or ".txt" files
	 *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
	 *
	 *  // Copy using the filter
	 *  FileUtils.copyDirectory(srcDir, destDir, filter);
	 *  </pre>
	 *
	 * @param srcDir  an existing directory to copy, must not be <code>null</code>
	 * @param destDir  the new directory, must not be <code>null</code>
	 * @param filter  the filter to apply, null means copy all directories and files
	 *  should be the same as the original
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @since Commons IO 1.4
	 */
	public static void copyDirectory(File srcDir, File destDir, FileFilter filter) throws IOException {
		copyDirectory(srcDir, destDir, filter, true);
	}

	/**
	 * Copies a filtered directory to a new location.
	 * <p>
	 * This method copies the contents of the specified source directory
	 * to within the specified destination directory.
	 * <p>
	 * The destination directory is created if it does not exist.
	 * If the destination directory did exist, then this method merges
	 * the source with the destination, with the source taking precedence.
	 *
	 * <h4>Example: Copy directories only</h4> 
	 *  <pre>
	 *  // only copy the directory structure
	 *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
	 *  </pre>
	 *
	 * <h4>Example: Copy directories and txt files</h4>
	 *  <pre>
	 *  // Create a filter for ".txt" files
	 *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
	 *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
	 *
	 *  // Create a filter for either directories or ".txt" files
	 *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
	 *
	 *  // Copy using the filter
	 *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
	 *  </pre>
	 * 
	 * @param srcDir  an existing directory to copy, must not be <code>null</code>
	 * @param destDir  the new directory, must not be <code>null</code>
	 * @param filter  the filter to apply, null means copy all directories and files
	 * @param preserveFileDate  true if the file date of the copy
	 *  should be the same as the original
	 *
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs during copying
	 * @since Commons IO 1.4
	 */
	public static void copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate)
			throws IOException {
		if (srcDir == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (srcDir.exists() == false) {
			throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
		}
		if (srcDir.isDirectory() == false) {
			throw new IOException("Source '" + srcDir + "' exists but is not a directory");
		}
		if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
			throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
		}

		// Cater for destination being directory within the source directory (see IO-141)
		List exclusionList = null;
		if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
			File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
			if (srcFiles != null && srcFiles.length > 0) {
				exclusionList = new ArrayList(srcFiles.length);
				for (int i = 0; i < srcFiles.length; i++) {
					File copiedFile = new File(destDir, srcFiles[i].getName());
					exclusionList.add(copiedFile.getCanonicalPath());
				}
			}
		}
		doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
	}

	/**
	 * Internal copy directory method.
	 * 
	 * @param srcDir  the validated source directory, must not be <code>null</code>
	 * @param destDir  the validated destination directory, must not be <code>null</code>
	 * @param filter  the filter to apply, null means copy all directories and files
	 * @param preserveFileDate  whether to preserve the file date
	 * @param exclusionList  List of files and directories to exclude from the copy, may be null
	 * @throws IOException if an error occurs
	 * @since Commons IO 1.1
	 */
	private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate,
			List exclusionList) throws IOException {
		if (destDir.exists()) {
			if (destDir.isDirectory() == false) {
				throw new IOException("Destination '" + destDir + "' exists but is not a directory");
			}
		} else {
			if (destDir.mkdirs() == false) {
				throw new IOException("Destination '" + destDir + "' directory cannot be created");
			}
			if (preserveFileDate) {
				destDir.setLastModified(srcDir.lastModified());
			}
		}
		if (destDir.canWrite() == false) {
			throw new IOException("Destination '" + destDir + "' cannot be written to");
		}
		// recurse
		File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
		if (files == null) { // null if security restricted
			throw new IOException("Failed to list contents of " + srcDir);
		}
		for (int i = 0; i < files.length; i++) {
			File copiedFile = new File(destDir, files[i].getName());
			if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
				if (files[i].isDirectory()) {
					doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
				} else {
					doCopyFile(files[i], copiedFile, preserveFileDate);
				}
			}
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Copies bytes from the URL <code>source</code> to a file
	 * <code>destination</code>. The directories up to <code>destination</code>
	 * will be created if they don't already exist. <code>destination</code>
	 * will be overwritten if it already exists.
	 *
	 * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
	 * @param destination  the non-directory <code>File</code> to write bytes to
	 *  (possibly overwriting), must not be <code>null</code>
	 * @throws IOException if <code>source</code> URL cannot be opened
	 * @throws IOException if <code>destination</code> is a directory
	 * @throws IOException if <code>destination</code> cannot be written
	 * @throws IOException if <code>destination</code> needs creating but can't be
	 * @throws IOException if an IO error occurs during copying
	 */
	public static void copyURLToFile(URL source, File destination) throws IOException {
		InputStream input = source.openStream();
		try {
			FileOutputStream output = openOutputStream(destination);
			try {
				IOUtils.copy(input, output);
			} finally {
				IOUtils.closeQuietly(output);
			}
		} finally {
			IOUtils.closeQuietly(input);
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Deletes a directory recursively. 
	 *
	 * @param directory  directory to delete
	 * @throws IOException in case deletion is unsuccessful
	 */
	public static void deleteDirectory(File directory) throws IOException {
		if (!directory.exists()) {
			return;
		}

		cleanDirectory(directory);
		if (!directory.delete()) {
			String message = "Unable to delete directory " + directory + ".";
			throw new IOException(message);
		}
	}

	/**
	 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
	 * <p>
	 * The difference between File.delete() and this method are:
	 * <ul>
	 * <li>A directory to be deleted does not have to be empty.</li>
	 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
	 * </ul>
	 *
	 * @param file  file or directory to delete, can be <code>null</code>
	 * @return <code>true</code> if the file or directory was deleted, otherwise
	 * <code>false</code>
	 *
	 * @since Commons IO 1.4
	 */
	public static boolean deleteQuietly(File file) {
		if (file == null) {
			return false;
		}
		try {
			if (file.isDirectory()) {
				cleanDirectory(file);
			}
		} catch (Exception e) {
		}

		try {
			return file.delete();
		} catch (Exception e) {
			return false;
		}
	}

	

	//-----------------------------------------------------------------------
	/**
	 * Reads the contents of a file into a String.
	 * The file is always closed.
	 *
	 * @param file  the file to read, must not be <code>null</code>
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @return the file contents, never <code>null</code>
	 * @throws IOException in case of an I/O error
	 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
	 */
	public static String readFileToString(File file, String encoding) throws IOException {
		InputStream in = null;
		try {
			in = openInputStream(file);
			return IOUtils.toString(in, encoding);
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	/**
	 * Reads the contents of a file into a String using the default encoding for the VM. 
	 * The file is always closed.
	 *
	 * @param file  the file to read, must not be <code>null</code>
	 * @return the file contents, never <code>null</code>
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.3.1
	 */
	public static String readFileToString(File file) throws IOException {
		return readFileToString(file, null);
	}

	/**
	 * Reads the contents of a file into a byte array.
	 * The file is always closed.
	 *
	 * @param file  the file to read, must not be <code>null</code>
	 * @return the file contents, never <code>null</code>
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.1
	 */
	public static byte[] readFileToByteArray(File file) throws IOException {
		InputStream in = null;
		try {
			in = openInputStream(file);
			return IOUtils.toByteArray(in);
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	/**
	 * Reads the contents of a file line by line to a List of Strings.
	 * The file is always closed.
	 *
	 * @param file  the file to read, must not be <code>null</code>
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @return the list of Strings representing each line in the file, never <code>null</code>
	 * @throws IOException in case of an I/O error
	 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
	 * @since Commons IO 1.1
	 */
	public static List readLines(File file, String encoding) throws IOException {
		InputStream in = null;
		try {
			in = openInputStream(file);
			return IOUtils.readLines(in, encoding);
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	/**
	 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
	 * The file is always closed.
	 *
	 * @param file  the file to read, must not be <code>null</code>
	 * @return the list of Strings representing each line in the file, never <code>null</code>
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.3
	 */
	public static List readLines(File file) throws IOException {
		return readLines(file, null);
	}

	/**
	 * Returns an Iterator for the lines in a <code>File</code>.
	 * <p>
	 * This method opens an <code>InputStream</code> for the file.
	 * When you have finished with the iterator you should close the stream
	 * to free internal resources. This can be done by calling the
	 * {@link LineIterator#close()} or
	 * {@link LineIterator#closeQuietly(LineIterator)} method.
	 * <p>
	 * The recommended usage pattern is:
	 * <pre>
	 * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
	 * try {
	 *   while (it.hasNext()) {
	 *     String line = it.nextLine();
	 *     /// do something with line
	 *   }
	 * } finally {
	 *   LineIterator.closeQuietly(iterator);
	 * }
	 * </pre>
	 * <p>
	 * If an exception occurs during the creation of the iterator, the
	 * underlying stream is closed.
	 *
	 * @param file  the file to open for input, must not be <code>null</code>
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @return an Iterator of the lines in the file, never <code>null</code>
	 * @throws IOException in case of an I/O error (file closed)
	 * @since Commons IO 1.2
	 */
	public static LineIterator lineIterator(File file, String encoding) throws IOException {
		InputStream in = null;
		try {
			in = openInputStream(file);
			return IOUtils.lineIterator(in, encoding);
		} catch (IOException ex) {
			IOUtils.closeQuietly(in);
			throw ex;
		} catch (RuntimeException ex) {
			IOUtils.closeQuietly(in);
			throw ex;
		}
	}

	/**
	 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
	 *
	 * @param file  the file to open for input, must not be <code>null</code>
	 * @return an Iterator of the lines in the file, never <code>null</code>
	 * @throws IOException in case of an I/O error (file closed)
	 * @since Commons IO 1.3
	 * @see #lineIterator(File, String)
	 */
	public static LineIterator lineIterator(File file) throws IOException {
		return lineIterator(file, null);
	}

	//-----------------------------------------------------------------------
	/**
	 * Writes a String to a file creating the file if it does not exist.
	 *
	 * NOTE: As from v1.3, the parent directories of the file will be created
	 * if they do not exist.
	 *
	 * @param file  the file to write
	 * @param data  the content to write to the file
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @throws IOException in case of an I/O error
	 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
	 */
	public static void writeStringToFile(File file, String data, String encoding) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			IOUtils.write(data, out, encoding);
		} finally {
			IOUtils.closeQuietly(out);
		}
	}

	/**
	 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
	 * 
	 * @param file  the file to write
	 * @param data  the content to write to the file
	 * @throws IOException in case of an I/O error
	 */
	public static void writeStringToFile(File file, String data) throws IOException {
		writeStringToFile(file, data, null);
	}

	/**
	 * Writes a byte array to a file creating the file if it does not exist.
	 * <p>
	 * NOTE: As from v1.3, the parent directories of the file will be created
	 * if they do not exist.
	 *
	 * @param file  the file to write to
	 * @param data  the content to write to the file
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.1
	 */
	public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			out.write(data);
		} finally {
			IOUtils.closeQuietly(out);
		}
	}

	/**
	 * Writes the <code>toString()</code> value of each item in a collection to
	 * the specified <code>File</code> line by line.
	 * The specified character encoding and the default line ending will be used.
	 * <p>
	 * NOTE: As from v1.3, the parent directories of the file will be created
	 * if they do not exist.
	 *
	 * @param file  the file to write to
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @param lines  the lines to write, <code>null</code> entries produce blank lines
	 * @throws IOException in case of an I/O error
	 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
	 * @since Commons IO 1.1
	 */
	public static void writeLines(File file, String encoding, Collection lines) throws IOException {
		writeLines(file, encoding, lines, null);
	}

	/**
	 * Writes the <code>toString()</code> value of each item in a collection to
	 * the specified <code>File</code> line by line.
	 * The default VM encoding and the default line ending will be used.
	 *
	 * @param file  the file to write to
	 * @param lines  the lines to write, <code>null</code> entries produce blank lines
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.3
	 */
	public static void writeLines(File file, Collection lines) throws IOException {
		writeLines(file, null, lines, null);
	}

	/**
	 * Writes the <code>toString()</code> value of each item in a collection to
	 * the specified <code>File</code> line by line.
	 * The specified character encoding and the line ending will be used.
	 * <p>
	 * NOTE: As from v1.3, the parent directories of the file will be created
	 * if they do not exist.
	 *
	 * @param file  the file to write to
	 * @param encoding  the encoding to use, <code>null</code> means platform default
	 * @param lines  the lines to write, <code>null</code> entries produce blank lines
	 * @param lineEnding  the line separator to use, <code>null</code> is system default
	 * @throws IOException in case of an I/O error
	 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
	 * @since Commons IO 1.1
	 */
	public static void writeLines(File file, String encoding, Collection lines, String lineEnding) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			IOUtils.writeLines(lines, lineEnding, out, encoding);
		} finally {
			IOUtils.closeQuietly(out);
		}
	}

	/**
	 * Writes the <code>toString()</code> value of each item in a collection to
	 * the specified <code>File</code> line by line.
	 * The default VM encoding and the specified line ending will be used.
	 *
	 * @param file  the file to write to
	 * @param lines  the lines to write, <code>null</code> entries produce blank lines
	 * @param lineEnding  the line separator to use, <code>null</code> is system default
	 * @throws IOException in case of an I/O error
	 * @since Commons IO 1.3
	 */
	public static void writeLines(File file, Collection lines, String lineEnding) throws IOException {
		writeLines(file, null, lines, lineEnding);
	}

	//-----------------------------------------------------------------------
	/**
	 * Deletes a file. If file is a directory, delete it and all sub-directories.
	 * <p>
	 * The difference between File.delete() and this method are:
	 * <ul>
	 * <li>A directory to be deleted does not have to be empty.</li>
	 * <li>You get exceptions when a file or directory cannot be deleted.
	 *      (java.io.File methods returns a boolean)</li>
	 * </ul>
	 *
	 * @param file  file or directory to delete, must not be <code>null</code>
	 * @throws NullPointerException if the directory is <code>null</code>
	 * @throws FileNotFoundException if the file was not found
	 * @throws IOException in case deletion is unsuccessful
	 */
	public static void forceDelete(File file) throws IOException {
		if (file.isDirectory()) {
			deleteDirectory(file);
		} else {
			boolean filePresent = file.exists();
			if (!file.delete()) {
				if (!filePresent) {
					throw new FileNotFoundException("File does not exist: " + file);
				}
				String message = "Unable to delete file: " + file;
				throw new IOException(message);
			}
		}
	}

	/**
	 * Schedules a file to be deleted when JVM exits.
	 * If file is directory delete it and all sub-directories.
	 *
	 * @param file  file or directory to delete, must not be <code>null</code>
	 * @throws NullPointerException if the file is <code>null</code>
	 * @throws IOException in case deletion is unsuccessful
	 */
	public static void forceDeleteOnExit(File file) throws IOException {
		if (file.isDirectory()) {
			deleteDirectoryOnExit(file);
		} else {
			file.deleteOnExit();
		}
	}

	/**
	 * Schedules a directory recursively for deletion on JVM exit.
	 *
	 * @param directory  directory to delete, must not be <code>null</code>
	 * @throws NullPointerException if the directory is <code>null</code>
	 * @throws IOException in case deletion is unsuccessful
	 */
	private static void deleteDirectoryOnExit(File directory) throws IOException {
		if (!directory.exists()) {
			return;
		}

		cleanDirectoryOnExit(directory);
		directory.deleteOnExit();
	}

	/**
	 * Cleans a directory without deleting it.
	 *
	 * @param directory  directory to clean, must not be <code>null</code>
	 * @throws NullPointerException if the directory is <code>null</code>
	 * @throws IOException in case cleaning is unsuccessful
	 */
	private static void cleanDirectoryOnExit(File directory) throws IOException {
		if (!directory.exists()) {
			String message = directory + " does not exist";
			throw new IllegalArgumentException(message);
		}

		if (!directory.isDirectory()) {
			String message = directory + " is not a directory";
			throw new IllegalArgumentException(message);
		}

		File[] files = directory.listFiles();
		if (files == null) { // null if security restricted
			throw new IOException("Failed to list contents of " + directory);
		}

		IOException exception = null;
		for (int i = 0; i < files.length; i++) {
			File file = files[i];
			try {
				forceDeleteOnExit(file);
			} catch (IOException ioe) {
				exception = ioe;
			}
		}

		if (null != exception) {
			throw exception;
		}
	}

	/**
	 * Makes a directory, including any necessary but nonexistent parent
	 * directories. If there already exists a file with specified name or
	 * the directory cannot be created then an exception is thrown.
	 *
	 * @param directory  directory to create, must not be <code>null</code>
	 * @throws NullPointerException if the directory is <code>null</code>
	 * @throws IOException if the directory cannot be created
	 */
	public static void forceMkdir(File directory) throws IOException {
		if (directory.exists()) {
			if (directory.isFile()) {
				String message = "File " + directory + " exists and is "
						+ "not a directory. Unable to create directory.";
				throw new IOException(message);
			}
		} else {
			if (!directory.mkdirs()) {
				String message = "Unable to create directory " + directory;
				throw new IOException(message);
			}
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Counts the size of a directory recursively (sum of the length of all files).
	 *
	 * @param directory  directory to inspect, must not be <code>null</code>
	 * @return size of directory in bytes, 0 if directory is security restricted
	 * @throws NullPointerException if the directory is <code>null</code>
	 */
	public static long sizeOfDirectory(File directory) {
		if (!directory.exists()) {
			String message = directory + " does not exist";
			throw new IllegalArgumentException(message);
		}

		if (!directory.isDirectory()) {
			String message = directory + " is not a directory";
			throw new IllegalArgumentException(message);
		}

		long size = 0;

		File[] files = directory.listFiles();
		if (files == null) { // null if security restricted
			return 0L;
		}
		for (int i = 0; i < files.length; i++) {
			File file = files[i];

			if (file.isDirectory()) {
				size += sizeOfDirectory(file);
			} else {
				size += file.length();
			}
		}

		return size;
	}

	//-----------------------------------------------------------------------
	/**
	 * Tests if the specified <code>File</code> is newer than the reference
	 * <code>File</code>.
	 *
	 * @param file  the <code>File</code> of which the modification date must
	 * be compared, must not be <code>null</code>
	 * @param reference  the <code>File</code> of which the modification date
	 * is used, must not be <code>null</code>
	 * @return true if the <code>File</code> exists and has been modified more
	 * recently than the reference <code>File</code>
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
	 */
	public static boolean isFileNewer(File file, File reference) {
		if (reference == null) {
			throw new IllegalArgumentException("No specified reference file");
		}
		if (!reference.exists()) {
			throw new IllegalArgumentException("The reference file '" + file + "' doesn't exist");
		}
		return isFileNewer(file, reference.lastModified());
	}

	/**
	 * Tests if the specified <code>File</code> is newer than the specified
	 * <code>Date</code>.
	 * 
	 * @param file  the <code>File</code> of which the modification date
	 * must be compared, must not be <code>null</code>
	 * @param date  the date reference, must not be <code>null</code>
	 * @return true if the <code>File</code> exists and has been modified
	 * after the given <code>Date</code>.
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 * @throws IllegalArgumentException if the date is <code>null</code>
	 */
	public static boolean isFileNewer(File file, Date date) {
		if (date == null) {
			throw new IllegalArgumentException("No specified date");
		}
		return isFileNewer(file, date.getTime());
	}

	/**
	 * Tests if the specified <code>File</code> is newer than the specified
	 * time reference.
	 *
	 * @param file  the <code>File</code> of which the modification date must
	 * be compared, must not be <code>null</code>
	 * @param timeMillis  the time reference measured in milliseconds since the
	 * epoch (00:00:00 GMT, January 1, 1970)
	 * @return true if the <code>File</code> exists and has been modified after
	 * the given time reference.
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 */
	public static boolean isFileNewer(File file, long timeMillis) {
		if (file == null) {
			throw new IllegalArgumentException("No specified file");
		}
		if (!file.exists()) {
			return false;
		}
		return file.lastModified() > timeMillis;
	}

	//-----------------------------------------------------------------------
	/**
	 * Tests if the specified <code>File</code> is older than the reference
	 * <code>File</code>.
	 *
	 * @param file  the <code>File</code> of which the modification date must
	 * be compared, must not be <code>null</code>
	 * @param reference  the <code>File</code> of which the modification date
	 * is used, must not be <code>null</code>
	 * @return true if the <code>File</code> exists and has been modified before
	 * the reference <code>File</code>
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
	 */
	public static boolean isFileOlder(File file, File reference) {
		if (reference == null) {
			throw new IllegalArgumentException("No specified reference file");
		}
		if (!reference.exists()) {
			throw new IllegalArgumentException("The reference file '" + file + "' doesn't exist");
		}
		return isFileOlder(file, reference.lastModified());
	}

	/**
	 * Tests if the specified <code>File</code> is older than the specified
	 * <code>Date</code>.
	 * 
	 * @param file  the <code>File</code> of which the modification date
	 * must be compared, must not be <code>null</code>
	 * @param date  the date reference, must not be <code>null</code>
	 * @return true if the <code>File</code> exists and has been modified
	 * before the given <code>Date</code>.
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 * @throws IllegalArgumentException if the date is <code>null</code>
	 */
	public static boolean isFileOlder(File file, Date date) {
		if (date == null) {
			throw new IllegalArgumentException("No specified date");
		}
		return isFileOlder(file, date.getTime());
	}

	/**
	 * Tests if the specified <code>File</code> is older than the specified
	 * time reference.
	 *
	 * @param file  the <code>File</code> of which the modification date must
	 * be compared, must not be <code>null</code>
	 * @param timeMillis  the time reference measured in milliseconds since the
	 * epoch (00:00:00 GMT, January 1, 1970)
	 * @return true if the <code>File</code> exists and has been modified before
	 * the given time reference.
	 * @throws IllegalArgumentException if the file is <code>null</code>
	 */
	public static boolean isFileOlder(File file, long timeMillis) {
		if (file == null) {
			throw new IllegalArgumentException("No specified file");
		}
		if (!file.exists()) {
			return false;
		}
		return file.lastModified() < timeMillis;
	}

	//-----------------------------------------------------------------------
	/**
	 * Computes the checksum of a file using the CRC32 checksum routine.
	 * The value of the checksum is returned.
	 *
	 * @param file  the file to checksum, must not be <code>null</code>
	 * @return the checksum value
	 * @throws NullPointerException if the file or checksum is <code>null</code>
	 * @throws IllegalArgumentException if the file is a directory
	 * @throws IOException if an IO error occurs reading the file
	 * @since Commons IO 1.3
	 */
	public static long checksumCRC32(File file) throws IOException {
		CRC32 crc = new CRC32();
		checksum(file, crc);
		return crc.getValue();
	}

	/**
	 * Computes the checksum of a file using the specified checksum object.
	 * Multiple files may be checked using one <code>Checksum</code> instance
	 * if desired simply by reusing the same checksum object.
	 * For example:
	 * <pre>
	 *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
	 * </pre>
	 *
	 * @param file  the file to checksum, must not be <code>null</code>
	 * @param checksum  the checksum object to be used, must not be <code>null</code>
	 * @return the checksum specified, updated with the content of the file
	 * @throws NullPointerException if the file or checksum is <code>null</code>
	 * @throws IllegalArgumentException if the file is a directory
	 * @throws IOException if an IO error occurs reading the file
	 * @since Commons IO 1.3
	 */
	public static Checksum checksum(File file, Checksum checksum) throws IOException {
		if (file.isDirectory()) {
			throw new IllegalArgumentException("Checksums can't be computed on directories");
		}
		InputStream in = null;
		try {
			in = new CheckedInputStream(new FileInputStream(file), checksum);
			IOUtils.copy(in, new NullOutputStream());
		} finally {
			IOUtils.closeQuietly(in);
		}
		return checksum;
	}

	/**
	 * Moves a directory.
	 * <p>
	 * When the destination directory is on another file system, do a "copy and delete".
	 *
	 * @param srcDir the directory to be moved
	 * @param destDir the destination directory
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs moving the file
	 * @since Commons IO 1.4
	 */
	public static void moveDirectory(File srcDir, File destDir) throws IOException {
		if (srcDir == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (!srcDir.exists()) {
			throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
		}
		if (!srcDir.isDirectory()) {
			throw new IOException("Source '" + srcDir + "' is not a directory");
		}
		if (destDir.exists()) {
			throw new IOException("Destination '" + destDir + "' already exists");
		}
		boolean rename = srcDir.renameTo(destDir);
		if (!rename) {
			copyDirectory(srcDir, destDir);
			deleteDirectory(srcDir);
			if (srcDir.exists()) {
				throw new IOException("Failed to delete original directory '" + srcDir + "' after copy to '" + destDir
						+ "'");
			}
		}
	}

	/**
	 * Moves a directory to another directory.
	 *
	 * @param src the file to be moved
	 * @param destDir the destination file
	 * @param createDestDir If <code>true</code> create the destination directory,
	 * otherwise if <code>false</code> throw an IOException
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs moving the file
	 * @since Commons IO 1.4
	 */
	public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
		if (src == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination directory must not be null");
		}
		if (!destDir.exists() && createDestDir) {
			destDir.mkdirs();
		}
		if (!destDir.exists()) {
			throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir="
					+ createDestDir + "]");
		}
		if (!destDir.isDirectory()) {
			throw new IOException("Destination '" + destDir + "' is not a directory");
		}
		moveDirectory(src, new File(destDir, src.getName()));

	}

	/**
	 * Moves a file.
	 * <p>
	 * When the destination file is on another file system, do a "copy and delete".
	 *
	 * @param srcFile the file to be moved
	 * @param destFile the destination file
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs moving the file
	 * @since Commons IO 1.4
	 */
	public static void moveFile(File srcFile, File destFile) throws IOException {
		if (srcFile == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destFile == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (!srcFile.exists()) {
			throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
		}
		if (srcFile.isDirectory()) {
			throw new IOException("Source '" + srcFile + "' is a directory");
		}
		if (destFile.exists()) {
			throw new IOException("Destination '" + destFile + "' already exists");
		}
		if (destFile.isDirectory()) {
			throw new IOException("Destination '" + destFile + "' is a directory");
		}
		boolean rename = srcFile.renameTo(destFile);
		if (!rename) {
			copyFile(srcFile, destFile);
			if (!srcFile.delete()) {
				FileUtils.deleteQuietly(destFile);
				throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile
						+ "'");
			}
		}
	}

	/**
	 * Moves a file to a directory.
	 *
	 * @param srcFile the file to be moved
	 * @param destDir the destination file
	 * @param createDestDir If <code>true</code> create the destination directory,
	 * otherwise if <code>false</code> throw an IOException
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs moving the file
	 * @since Commons IO 1.4
	 */
	public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
		if (srcFile == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination directory must not be null");
		}
		if (!destDir.exists() && createDestDir) {
			destDir.mkdirs();
		}
		if (!destDir.exists()) {
			throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir="
					+ createDestDir + "]");
		}
		if (!destDir.isDirectory()) {
			throw new IOException("Destination '" + destDir + "' is not a directory");
		}
		moveFile(srcFile, new File(destDir, srcFile.getName()));
	}

	public static void moveFileToDirectory(Collection<File> files, File destDir, boolean createDestDir)
			throws IOException {
		if (files == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination directory must not be null");
		}
		for (Iterator<File> it = files.iterator(); it.hasNext();) {
			File next = it.next();
			moveFileToDirectory(next, destDir, createDestDir);

		}

	}

	/**
	 * Moves a file or directory to the destination directory.
	 * <p>
	 * When the destination is on another file system, do a "copy and delete".
	 *
	 * @param src the file or directory to be moved
	 * @param destDir the destination directory 
	 * @param createDestDir If <code>true</code> create the destination directory,
	 * otherwise if <code>false</code> throw an IOException
	 * @throws NullPointerException if source or destination is <code>null</code>
	 * @throws IOException if source or destination is invalid
	 * @throws IOException if an IO error occurs moving the file
	 * @since Commons IO 1.4
	 */
	public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
		if (src == null) {
			throw new NullPointerException("Source must not be null");
		}
		if (destDir == null) {
			throw new NullPointerException("Destination must not be null");
		}
		if (!src.exists()) {
			throw new FileNotFoundException("Source '" + src + "' does not exist");
		}
		if (src.isDirectory()) {
			moveDirectoryToDirectory(src, destDir, createDestDir);
		} else {
			moveFileToDirectory(src, destDir, createDestDir);
		}
	}

	//-----------------------------------------------------------------------
	/**
	 * Waits for NFS to propagate a file creation, imposing a timeout.
	 * <p>
	 * This method repeatedly tests {@link File#exists()} until it returns
	 * true up to the maximum time specified in seconds.
	 *
	 * @param file  the file to check, must not be <code>null</code>
	 * @param seconds  the maximum time in seconds to wait
	 * @return true if file exists
	 * @throws NullPointerException if the file is <code>null</code>
	 */
	public static boolean waitFor(File file, int seconds) {
		int timeout = 0;
		int tick = 0;
		while (!file.exists()) {
			if (tick++ >= 10) {
				tick = 0;
				if (timeout++ > seconds) {
					return false;
				}
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException ignore) {
				// ignore exception
			} catch (Exception ex) {
				break;
			}
		}
		return true;
	}

	//-----------------------------------------------------------------------
	/**
	 * Compares the contents of two files to determine if they are equal or not.
	 * <p>
	 * This method checks to see if the two files are different lengths
	 * or if they point to the same file, before resorting to byte-by-byte
	 * comparison of the contents.
	 * <p>
	 * Code origin: Avalon
	 *
	 * @param file1  the first file
	 * @param file2  the second file
	 * @return true if the content of the files are equal or they both don't
	 * exist, false otherwise
	 * @throws IOException in case of an I/O error
	 */
	public static boolean contentEquals(File file1, File file2) throws IOException {
		boolean file1Exists = file1.exists();
		if (file1Exists != file2.exists()) {
			return false;
		}

		if (!file1Exists) {
			// two not existing files are equal
			return true;
		}

		if (file1.isDirectory() || file2.isDirectory()) {
			// don't want to compare directory contents
			throw new IOException("Can't compare directories, only files");
		}

		if (file1.length() != file2.length()) {
			// lengths differ, cannot be equal
			return false;
		}

		if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
			// same file
			return true;
		}

		InputStream input1 = null;
		InputStream input2 = null;
		try {
			input1 = new FileInputStream(file1);
			input2 = new FileInputStream(file2);
			return IOUtils.contentEquals(input1, input2);

		} finally {
			IOUtils.closeQuietly(input1);
			IOUtils.closeQuietly(input2);
		}
	}

	/**
	 * Reads a text file.
	 *  
	 * @param fileName the name of the text file
	 * @return the lines of the text file
	 * @throws FileNotFoundException when the file was not found
	 * @throws IOException when file could not be read.
	 */
	public static String[] readTextFile(String fileName) throws FileNotFoundException, IOException {
		return readTextFile(new File(fileName));
	}

	/**
	* Checks, whether the child directory is a subdirectory of the base 
	* directory.
	*
	* @param base the base directory.
	* @param child the suspected child directory.
	* @return true, if the child is a subdirectory of the base directory.
	* @throws IOException if an IOError occured during the test.
	*/
	public boolean isSubDirectory(File base, File child) throws IOException {
		base = base.getCanonicalFile();
		child = child.getCanonicalFile();

		File parentFile = child;
		while (parentFile != null) {
			if (base.equals(parentFile)) {
				return true;
			}
			parentFile = parentFile.getParentFile();
		}
		return false;
	}

	/**
	 * Changes the filename suffix.
	 *
	 * @param filename the filename to be changed
	 * @param suffix the new suffix of the file
	 * @return the filename with the replaced suffix
	 */
	public static String changeFileNameSuffixTo(String filename, String suffix) {

		int dotPos = filename.lastIndexOf('.');
		if (dotPos != -1) {
			return filename.substring(0, dotPos + 1) + suffix;
		} else {
			// the string has no suffix
			return filename;
		}
	}

	/**
	* 得到唯一的文件 如果存在生成一个新的随机名字的文件.
	*/
	public final static File getUniqueFile(File oldFile) {
		File newFile = oldFile;
		while (true) {
			if (!newFile.exists()) {
				break;
			}
			newFile = new File(oldFile.getAbsolutePath() + '.' + Math.abs(RANDOM_GEN.nextLong()));
		}
		return newFile;
	}

	private final static Random RANDOM_GEN = new Random(System.currentTimeMillis());

	/**
	 * 尝试删除文件(非目录)尝试3次
	 * @param file
	 */
	public static void deleteFiles(File file) {
		int maxTry = 3;
		while (maxTry > 0) {
			maxTry--;
			if (file.isFile()) {
				if (file.delete())
					return;
				else
					continue;
			} else {
				return;
			}
		}
	}

	public static void writeFile(File file, byte[] data) throws IOException {
		final int MAX_BUFFER_SIZE = 4096;
		FileOutputStream output = null;
		FileChannel fc = null;
		try {
			output = new FileOutputStream(file);
			fc = output.getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(MAX_BUFFER_SIZE);
			int offset = 0;
			while (offset < data.length) {
				buffer.clear();
				int len = data.length - offset;
				if (len > MAX_BUFFER_SIZE)
					len = MAX_BUFFER_SIZE;
				buffer.put(data, offset, len);
				offset += len;
				buffer.flip();
				fc.write(buffer);
			}
		} finally {
			if (fc != null) {
				try {
					fc.close();
				} catch (IOException e) {
				}
			}
			if (output != null) {
				try {
					output.close();
				} catch (IOException e) {
				}
			}
		}
	}

	public static void readFile(File file, OutputStream output) throws IOException {
		FileInputStream input = null;
		FileChannel fc = null;
		try {
			input = new FileInputStream(file);
			fc = input.getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(4096);
			for (;;) {
				buffer.clear();
				int n = fc.read(buffer);
				if (n == (-1))
					break;
				output.write(buffer.array(), 0, buffer.position());
			}
		} finally {
			if (fc != null) {
				try {
					fc.close();
				} catch (IOException e) {
				}
			}
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
				}
			}
		}
	}

	/**
	 * Returns the system-dependent version of the given file or path. This is
	 * done by converting all the slash characters to the correct slash for the
	 * current operating system
	 *
	 * @param   path The path to convert to the current operating system
	 * @return  The converted path
	 */
	public static String convertPath(String path) {

		String newPath;
		if (File.separatorChar == '\\') {
			newPath = path.replace('/', '\\');
		} else {
			newPath = path.replace('\\', '/');
		}

		return newPath;
	}

	/**
	 * Reads a text file.
	 *  
	 * @param file the text file
	 * @return the lines of the text file
	 * @throws FileNotFoundException when the file was not found
	 * @throws IOException when file could not be read.
	 */
	public static String[] readTextFile(File file) throws FileNotFoundException, IOException {
		ArrayList<String> lines = new ArrayList<String>();
		BufferedReader in = new BufferedReader(new FileReader(file));
		String line;
		while ((line = in.readLine()) != null) {
			lines.add(line);
		}
		in.close();
		return (String[]) lines.toArray(new String[lines.size()]);
	}

	/**
	 * Reads a text file.
	 *  
	 * @param file the text file
	 * @param encoding the encoding of the textfile
	 * @return the lines of the text file
	 * @throws FileNotFoundException when the file was not found
	 * @throws IOException when file could not be read.
	 */
	public static String[] readTextFile(File file, String encoding) throws FileNotFoundException, IOException {
		return readTextFile(new FileInputStream(file), encoding);
	}
	/**
	 * 重命名文件名
	 * @param srcFileName 源文件名
	 * @param destFileName 目标文件名
	 * @return 重命名成功:true 重命名失败:false
	 */
	public static boolean rename(File src,File dest) {
		if (src.exists()) {
			return src.renameTo(dest);
		}
		return false;
	}

	/**
	 * Reads the text from the given input stream in the default encoding.
	 * 
	 * @param in the input stream
	 * @return the text contained in the stream
	 * @throws IOException when stream could not be read.
	 */
	public static String[] readTextFile(InputStream in) throws IOException {
		return readTextFile(in, null);
	}

	/**
	 * Reads the text from the given input stream in the default encoding.
	 * 
	 * @param in the input stream
	 * @param encoding the encoding of the textfile
	 * @return the text contained in the stream
	 * @throws IOException when stream could not be read.
	 */
	public static String[] readTextFile(InputStream in, String encoding) throws IOException {
		ArrayList lines = new ArrayList();
		BufferedReader bufferedIn;
		if (encoding != null) {
			bufferedIn = new BufferedReader(new InputStreamReader(in, encoding));
		} else {
			bufferedIn = new BufferedReader(new InputStreamReader(in));
		}
		String line;
		while ((line = bufferedIn.readLine()) != null) {
			lines.add(line);
		}
		bufferedIn.close();
		in.close();
		return (String[]) lines.toArray(new String[lines.size()]);
	}

	/**
	 * Writes (and creates) a text file.
	 * 
	 * @param file the file to which the text should be written
	 * @param lines the text lines of the file in a collection with String-values
	 * @throws IOException when there is an input/output error during the saving
	 */
	public static void writeTextFile(File file, Collection lines) throws IOException {
		writeTextFile(file, (String[]) lines.toArray(new String[lines.size()]));
	}

	/**
	 * Writes (and creates) a text file.
	 * 
	 * @param file the file to which the text should be written
	 * @param lines the text lines of the file
	 * @throws IOException when there is an input/output error during the saving
	 */
	public static void writeTextFile(File file, String[] lines) throws IOException {
		File parentDir = file.getParentFile();
		if ((parentDir != null) && !parentDir.exists()) {
			parentDir.mkdirs();
		}
		PrintWriter out = new PrintWriter(new FileWriter(file));
		for (int i = 0; i < lines.length; i++) {
			out.println(lines[i]);
		}
		out.close();
	}

	/**
	 * Writes (and creates) a text file.
	 * 
	 * @param file the file to which the text should be written
	 * @param lines the text lines of the file
	 * @throws IOException when there is an input/output error during the saving
	 */
	public static void writeTextFile(File file, byte aBytes[]) throws IOException {
		FileOutputStream outputStream = new FileOutputStream(file);
		IOUtils.write(aBytes, outputStream);
		IOUtils.close(outputStream);
	}

	/**
	 * Returns the number of bytes contained within aFile.
	 * @param aFile
	 * @return
	 * @throws IOException
	 */

	public static int getNumberOfBytes(File aFile) throws IOException {
		int size = 0;
		FileInputStream is = new FileInputStream(aFile);
		while (is.read() != -1) {
			size++;
		}
		is.close();
		return size;
	}

	/**
	 * Returns the contents of aFile as an array of bytes. 
	 * @param aFile
	 * @return
	 * @throws IOException
	 */
	public static byte[] getContents(File aFile) throws IOException {
		FileInputStream is;
		byte contents[] = new byte[getNumberOfBytes(aFile)];
		is = new FileInputStream(aFile);
		is.read(contents);
		is.close();
		return contents;
	}

	/**
	 * Returns the contents of the file at aPath as an array of bytes.
	 * @param aPath
	 * @return
	 * @throws IOException
	 */
	public static byte[] getContents(String aPath) throws IOException {
		return getContents(new File(aPath));
	}

	/**
	 * Returns the contents of the file at aPath as a String.
	 * @param aPath
	 * @return
	 * @throws IOException
	 */
	public static String getTextContents(String aPath) throws IOException {
		return getTextContents(new File(aPath));
	}

	/**
	 * Returns the contents of aFile as a String.
	 * @param aPath
	 * @return
	 * @throws IOException
	 */
	public static String getTextContents(File aFile) throws IOException {
		int ch;
		StringBuffer sb = new StringBuffer();
		FileInputStream is = new FileInputStream(aFile);
		while ((ch = is.read()) != -1) {
			sb.append((char) ch);
		}
		is.close();
		return sb.toString();
	}

	/**
	* Insert a String in a file
	* 
	* @param inFile file to insert String to
	* @param lineno position in file
	* @param lineToBeInserted String to be inserted
	* @param replace replace/append mode
	* 
	*/
	public static boolean insertStringInFile(File inFile, int lineno, String lineToBeInserted, boolean replace) {

		System.out.println("Inserting line [" + lineToBeInserted + "] into [" + inFile.getAbsolutePath() + "] at pos ["
				+ lineno + "]");
		boolean success = true;
		// temp file
		File outFile = new File("~.tmp");

		try {
			// input
			FileInputStream fis = new FileInputStream(inFile);
			BufferedReader in = new BufferedReader(new InputStreamReader(fis));
			// output         
			FileOutputStream fos = new FileOutputStream(outFile);
			PrintWriter out = new PrintWriter(fos);
			String thisLine = "";
			int i = 1;
			while ((thisLine = in.readLine()) != null) {
				if (i == lineno) {
					out.println(lineToBeInserted);
					//System.out.println("inserted ["+lineToBeInserted+"] at pos ["+lineno+"]");
				}
				if (!replace) {
					out.println(thisLine);
				}
				i++;
			}
			out.flush();
			in.close();
			// re-output to in         
			fos = new FileOutputStream(inFile);
			out = new PrintWriter(fos);
			// input
			fis = new FileInputStream(outFile);
			in = new BufferedReader(new InputStreamReader(fis));
			while ((thisLine = in.readLine()) != null) {
				out.println(thisLine);
			}
			out.flush();
			out.close();
			in.close();
		} catch (FileNotFoundException e) {
			log.error("File [" + inFile.getAbsolutePath() + "] not found !");
			success = false;
		} catch (IOException e) {
			log.error("I/O exception occured while inserting line into [" + inFile.getAbsolutePath() + "]");
			success = false;
		}
		return success;
	}

	public static long getFileOrDirectorySize(File file) {
		if (file.isFile()) {
			return file.length();
		} else {
			long res = 0;
			File[] files = file.listFiles();
			if (files != null) {
				for (int i = 0; i < files.length; i++) {
					res += getFileOrDirectorySize(files[i]);
				}
			}
			return res;
		}
	}

	public static String guessContentTypeFromFileType(String file_type) {
		String response_type = null;

		if (file_type == null) {

			response_type = "application/octet-stream";

		} else {

			if (file_type.equals("html") || file_type.equals("htm")) {
				response_type = "text/html";
			} else if (file_type.equals("css")) {
				response_type = "text/css";
			} else if (file_type.equals("xml")) {
				response_type = "text/xml";
			} else if (file_type.equals("xsl")) {
				response_type = "text/xml";
			} else if (file_type.equals("jpg") || file_type.equals("jpeg")) {
				response_type = "image/jpeg";
			} else if (file_type.equals("gif")) {
				response_type = "image/gif";
			} else if (file_type.equals("tiff")) {
				response_type = "image/tiff";
			} else if (file_type.equals("bmp")) {
				response_type = "image/bmp";
			} else if (file_type.equals("png")) {
				response_type = "image/png";
			} else if (file_type.equals("torrent") || file_type.equals("tor")) {
				response_type = "application/x-bittorrent";
			} else if (file_type.equals("zip")) {
				response_type = "application/zip";
			} else if (file_type.equals("txt")) {
				response_type = "text/plain";
			} else if (file_type.equals("jar")) {
				response_type = "application/java-archive";
			} else if (file_type.equals("jnlp")) {
				response_type = "application/x-java-jnlp-file";
			} else if (file_type.equals("mp3")) {
				response_type = "audio/x-mpeg";
			} else {
				response_type = "application/octet-stream";
			}
		}

		return (response_type);
	}

	public static double getSize(String filename) {
		if (!StringUtils.isEmpty(filename)) {
			return (getSize(new File(filename)));
		} else {
			return (0);
		}
	}

	public static double getSize(File file) {
		double size = 0;
		if (file != null) {
			if (file.isFile()) {
				size = (double) file.length() / (double) KILOBYTE;
				return (size);
			} else {
				File[] subFiles = file.listFiles();
				for (int i = 0; i < subFiles.length; i++) {
					if (subFiles[i].isFile()) {
						size += (double) subFiles[i].length() / (double) KILOBYTE;
					} else {
						size += getSize(subFiles[i]);
					}
				}
			}
		}
		return (size);
	}
 
	/**
	 * 功能描述：拷贝一个目录或者文件到指定路径下，即把源文件拷贝到目标文件路径下
	 * 
	 * @param source
	 *            源文件
	 * @param target
	 *            目标文件路径
	 * @return void
	 */
	public static void copy1(File source, File target) {
		File tarpath = new File(target, source.getName());
		if (source.isDirectory()) {
			tarpath.mkdir();
			File[] dir = source.listFiles();
			for (int i = 0; i < dir.length; i++) {
				copy1(dir[i], tarpath);
			}
		} else {
			try {
				InputStream is = new FileInputStream(source); // 用于读取文件的原始字节流
				OutputStream os = new FileOutputStream(tarpath); // 用于写入文件的原始字节的流
				byte[] buf = new byte[1024];// 存储读取数据的缓冲区大小
				int len = 0;
				while ((len = is.read(buf)) != -1) {
					os.write(buf, 0, len);
				}
				is.close();
				os.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**   
	  
	 * 压缩文件   
	
	 * @param inputFileName 要压缩的文件或文件夹路径，例如：c:\\a.txt,c:\\a\   
	
	 * @param outputFileName 输出zip文件的路径，例如：c:\\a.zip   
	
	 */

	public static void zip(String inputFileName, String outputFileName) throws Exception {
		ZipUtils.zip(inputFileName, outputFileName);

	}

	/**以文件流的方式复制文件 
	 * 该方法经过测试，支持中文处理，并且可以复制多种类型，比如txt，xml，jpg，doc等多种格式 
	* @param src 文件源目录 
	* @param dest 文件目的目录 
	* @throws IOException 
	*/
	public void copyFileByStream(String src, String dest) throws IOException {
		FileInputStream in = new FileInputStream(src);
		File file = new File(dest);
		if (!file.exists())
			file.createNewFile();
		FileOutputStream out = new FileOutputStream(file);
		int c;
		byte buffer[] = new byte[1024];
		while ((c = in.read(buffer)) != -1) {
			for (int i = 0; i < c; i++)
				out.write(buffer[i]);
		}
		in.close();
		out.close();
	}

	/**
	 * 删除目录
	 * @param directory
	 */
	public static void deltree(String directory) {
		deltree(new File(directory));
	}

	/**
	 * 删除目录
	 * @param directory
	 */
	public static void deltree(File directory) {
		if (directory.exists() && directory.isDirectory()) {
			File[] fileArray = directory.listFiles();

			for (int i = 0; i < fileArray.length; i++) {
				if (fileArray[i].isDirectory()) {
					deltree(fileArray[i]);
				} else {
					fileArray[i].delete();
				}
			}

			directory.delete();
		}
	}

	public static String getPath(String fullFileName) {
		int pos = fullFileName.lastIndexOf("/");

		if (pos == -1) {
			pos = fullFileName.lastIndexOf("\\");
		}

		String shortFileName = fullFileName.substring(0, pos);

		if (StringUtils.isBlank(shortFileName)) {
			return "/";
		}

		return shortFileName;
	}

	public static File[] sortFiles(File[] files) {
		Arrays.sort(files, new FileDateComparator());

		List<File> directoryList = new ArrayList<File>();
		List<File> fileList = new ArrayList<File>();

		for (int i = 0; i < files.length; i++) {
			if (files[i].isDirectory()) {
				directoryList.add(files[i]);
			} else {
				fileList.add(files[i]);
			}
		}

		directoryList.addAll(fileList);

		return (File[]) directoryList.toArray(new File[0]);
	}

	public static void copyFileByNIO(File source, File destination) {
		if (!source.exists()) {
			return;
		}

		if ((destination.getParentFile() != null) && (!destination.getParentFile().exists())) {

			destination.getParentFile().mkdirs();
		}

		try {
			FileChannel srcChannel = new FileInputStream(source).getChannel();
			FileChannel dstChannel = new FileOutputStream(destination).getChannel();

			dstChannel.transferFrom(srcChannel, 0, srcChannel.size());

			srcChannel.close();
			dstChannel.close();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
	/**
	 * 初步测试比copy()和copy1快
	 * @param source
	 * @param target
	 * @throws IOException
	 */
	public static void copyByNIOWithMapBuff(File source, File target) throws IOException {
		FileChannel input = null;
		FileChannel output = null;

		try {
			input = new FileInputStream(source).getChannel();
			output = new FileOutputStream(target).getChannel();

			MappedByteBuffer buffer = input.map(FileChannel.MapMode.READ_ONLY, 0, input.size());
			output.write(buffer);
		} finally {
			if (input != null) {
				input.close();
			}

			if (output != null) {
				output.close();
			}
		}
	}

	public static void copyRecursive(File source, File target) throws IOException {
		if (source.isDirectory()) {
			if (!target.exists()) {
				target.mkdirs();
			}
			if (target.isDirectory()) {

				File[] files = source.listFiles();
				for (int i = 0; i < files.length; i++) {
					File file = files[i];
					File targetFile = new File(target, file.getName());
					if (file.isFile()) {
						if (targetFile.exists()) {
							targetFile.delete();
						}
						copy(file, targetFile);
					} else {
						targetFile.mkdirs();
						copyRecursive(file, targetFile);
					}
				}
			}
		} else {
			if (!target.isDirectory()) {
				if (!target.exists()) {
					File dir = target.getParentFile();
					if (!dir.exists() && !dir.mkdirs()) {
						throw new IOException("Could not create target directory: " + dir);
					}
					if (!target.createNewFile()) {
						throw new IOException("Could not create target file: " + target);
					}
				}
				copy(source, target);
			}
		}
	}

	public static void moveRecursive(File source, File target) throws IOException {
		// try fast file-system-level move/rename first
		if (source.renameTo(target))
			return;

		if (source.isDirectory()) {
			if (!target.exists()) {
				target.mkdirs();
			}
			if (target.isDirectory()) {

				File[] files = source.listFiles();
				for (int i = 0; i < files.length; i++) {
					File file = files[i];
					File targetFile = new File(target, file.getName());
					if (file.isFile()) {
						if (targetFile.exists()) {
							targetFile.delete();
						}
						if (!file.renameTo(targetFile)) {
							copy(file, targetFile);
							file.delete();
						}
					} else {
						if (!targetFile.exists()) {
							if (!targetFile.mkdirs()) {
								throw new IOException("Could not create target directory: " + targetFile);
							}
						}
						moveRecursive(file, targetFile);
					}
				}
				source.delete();
			}
		} else {
			if (!target.isDirectory()) {
				copy(source, target);
				source.delete();
			}
		}
	}

	public static void removeRecursive(File toRemove) {
		if (!toRemove.exists())
			return;
		if (toRemove.isDirectory()) {
			File fileList[] = toRemove.listFiles();
			for (int a = 0; a < fileList.length; a++) {
				removeRecursive(fileList[a]);
			}
		}
		toRemove.delete();
	}



	/**
	 * Creates a temporary file, using the class name to construct the prefix.  Suffix
	 * is the default.  The file is automatically set to delete on exit.
	 * @param c Class requesting temporary file
	 * @return Temporary file object, with deleteOnExit set
	 * @throws IOException Could not create file
	 * @see java.io.File#createTempFile(java.lang.String, java.lang.String)
	 */
	public static File createTempFile(Class c) throws IOException {
		return deleteTempFileOnExit(File.createTempFile("TMP_" + c.getSimpleName(), null));
	}

	/**
	 * Sets the file to delete on exit and returns the file.
	 * @param f File to set
	 * @return Same file object
	 * @see java.io.File#deleteOnExit()
	 */
	private static File deleteTempFileOnExit(File f) {
		f.deleteOnExit();
		return f;
	}

	public static ArrayList<String> getFileList(File file) {
		return getFileList(file, null, null, true);
	}

	public static ArrayList<String> getFileList(File file, Pattern included, Pattern excluded) {
		return getFileList(file, new Pattern[] { included }, new Pattern[] { excluded }, true);
	}

	public static ArrayList<String> getFileList(File file, Pattern[] included, Pattern[] excluded) {
		return getFileList(file, included, excluded, true);
	}

	private static ArrayList<String> getFileList(File file, Pattern[] included, Pattern[] excluded, boolean root) {
		if (null == file) {
			return new ArrayList<String>();
		}

		ArrayList<String> filelist = new ArrayList<String>();
		if (file.isDirectory()) {
			String[] list = file.list();
			if (null != list) {
				for (String list_entry : list) {
					File next_file = new File(file.getAbsolutePath() + File.separator + list_entry);
					ArrayList<String> dir = getFileList(next_file, included, excluded, false);

					for (String file_name : dir) {
						if (root) {
							// if the file is not accepted, don't process it further
							if (!StringUtils.filter(file_name, included, excluded)) {
								continue;
							}

						} else {
							file_name = file.getName() + File.separator + file_name;
						}

						int filelist_size = filelist.size();
						for (int j = 0; j < filelist_size; j++) {
							if (filelist.get(j).compareTo(file_name) > 0) {
								filelist.add(j, file_name);
								break;
							}
						}
						if (filelist.size() == filelist_size) {
							filelist.add(file_name);
						}
					}
				}
			}
		} else if (file.isFile()) {
			String file_name = file.getName();

			if (root) {
				if (StringUtils.filter(file_name, included, excluded)) {
					filelist.add(file_name);
				}
			} else {
				filelist.add(file_name);
			}
		}

		return filelist;
	}

	public static String convertPathToSystemSeperator(String path) {
		if (null == path)
			throw new IllegalArgumentException("path can't be null.");

		ArrayList<String> path_parts = StringUtils.splitToArrayList(path, "/");
		return StringUtils.join(path_parts, File.separator);
	}

	public static String getExtension(File file) {
		if(file==null)
			return "";
		return getExtension(file.getName());
	}

	public static String readString(File source, String encoding) throws IOException {
		if (null == source)
			throw new IllegalArgumentException("source can't be null.");

		try {
			FileInputStream file_input_stream = new FileInputStream(source);
			String content = null;
			if (null == encoding) {
				content = readString(file_input_stream);
			} else {
				content = readString(file_input_stream, encoding);
			}
			file_input_stream.close();
			return content;
		} catch (IOException e) {
			throw new IOException("Error while reading url '" + source.getAbsolutePath() + ".", e);
		}
	}

	public static void writeString(String content, File destination) throws IOException {
		if (null == content)
			throw new IllegalArgumentException("content can't be null.");
		if (null == destination)
			throw new IllegalArgumentException("destination can't be null.");

		try {
			FileWriter file_writer = null;

			file_writer = new FileWriter(destination);

			file_writer.write(content, 0, content.length());
			file_writer.flush();
			file_writer.close();
		} catch (IOException e) {
			throw new IOException("Error while write a string to '" + destination.getAbsolutePath() + ".", e);
		}
	}

	/**得到文件的扩展名.   
	 *  @param fileName    需要处理的文件的名字.     
	 *  @return the extension portion of the file's name.     */
	public static String getExtension(String fileName) {
		if (null == fileName)
			throw new IllegalArgumentException("fileName can't be null.");

		String ext = null;

		int index = fileName.lastIndexOf('.');
		if (index > 0 && index < fileName.length() - 1) {
			ext = fileName.substring(index + 1).toLowerCase();
		}else {
			ext="jpeg";
		}

		return ext;
	}

	/**得到文件的扩展名带点   
	 *  @param fileName    需要处理的文件的名字.     
	 *  @return the extension portion of the file's name.     */
	public static String getSuffix(String fileName) {
		if (null == fileName)
			throw new IllegalArgumentException("fileName can't be null.");

		String ext = null;

		int index = fileName.lastIndexOf('.');
		if (index > 0 && index < fileName.length() - 1) {
			ext = fileName.substring(index).toLowerCase();
		}

		return ext;
	}

	public static String getBaseName(File file) {
		return getBaseName(file.getName());
	}

	public static void deleteFile(File file) {
		if (null == file)
			throw new IllegalArgumentException("file can't be null.");

		file.delete();
	}

	public static String getBaseName(String fileName) {
		if (null == fileName)
			throw new IllegalArgumentException("fileName can't be null.");

		String basename = null;

		int index = fileName.lastIndexOf('.');
		if (index > 0 && index < fileName.length() - 1) {
			basename = fileName.substring(0, index);
		}

		return basename;
	}


	/**
	 * Copy the contents of the given InputStream to the given OutputStream.
	 * Closes both streams when done.
	 * @param in the stream to copy from
	 * @param out the stream to copy to
	 * @return the number of bytes copied
	 * @throws IOException in case of I/O errors
	 */
	public static int copy(InputStream in, OutputStream out) throws IOException {
		Assert.notNull(in, "No InputStream specified");
		Assert.notNull(out, "No OutputStream specified");
		try {
			int byteCount = 0;
			byte[] buffer = new byte[BUFFER_SIZE];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		}
		finally {
			try {
				in.close();
			}
			catch (IOException ex) {
				log.warn("Could not close InputStream", ex);
			}
			try {
				out.close();
			}
			catch (IOException ex) {
				log.warn("Could not close OutputStream", ex);
			}
		}
	}

	public static void copy(InputStream inputStream, File target) throws Exception {
		if (null == inputStream)
			throw new IllegalArgumentException("inputStream can't be null.");
		if (null == target)
			throw new IllegalArgumentException("target can't be null.");

		try {
			FileOutputStream file_output_stream = new FileOutputStream(target);

			copy(inputStream, file_output_stream);

			file_output_stream.close();
		} catch (IOException e) {
			throw new IOException("Error while copying an input stream to file '" + target.getAbsolutePath() + "'.", e);
		}
	}

	public static void copy(File source, OutputStream outputStream) throws Exception {
		if (null == source)
			throw new IllegalArgumentException("source can't be null.");
		if (null == outputStream)
			throw new IllegalArgumentException("outputStream can't be null.");

		try {
			FileInputStream file_input_stream = new FileInputStream(source);

			copy(file_input_stream, outputStream);

			file_input_stream.close();
		} catch (IOException e) {
			throw new IOException("Error while copying file '" + source.getAbsolutePath() + "' to an output stream.", e);
		}
	}

	public static ByteArrayOutputStream readStream(InputStream inputStream) throws IOException {
		if (null == inputStream)
			throw new IllegalArgumentException("inputStream can't be null.");

		try {
			byte[] buffer = new byte[1024];
			int return_value = -1;
			ByteArrayOutputStream output_stream = new ByteArrayOutputStream(buffer.length);

			return_value = inputStream.read(buffer);

			while (-1 != return_value) {
				output_stream.write(buffer, 0, return_value);
				return_value = inputStream.read(buffer);
			}

			output_stream.close();

			inputStream.close();

			return output_stream;
		} catch (IOException e) {
			throw new IOException("Error while reading the complete contents of an input stream.", e);
		}
	}

	public static String readString(InputStream inputStream) throws IOException {
		if (null == inputStream)
			throw new IllegalArgumentException("inputStream can't be null.");

		return readStream(inputStream).toString();
	}

	public static String readString(Reader reader) throws IOException {
		if (null == reader)
			throw new IllegalArgumentException("reader can't be null.");

		try {
			char[] buffer = new char[1024];
			StringBuilder result = new StringBuilder();

			int size = reader.read(buffer);
			while (size != -1) {
				result.append(buffer, 0, size);
				size = reader.read(buffer);
			}

			return result.toString();
		} catch (IOException e) {
			throw new IOException("Error while reading the complete contents of an reader.", e);
		}
	}

	public static String readString(InputStream inputStream, String encoding) throws IOException {
		if (null == inputStream)
			throw new IllegalArgumentException("inputStream can't be null.");

		try {
			return readStream(inputStream).toString(encoding);
		} catch (UnsupportedEncodingException e) {
			throw new IOException("Encoding '" + encoding + "' is not supported.", e);
		}
	}

	public static void deleteAll(File[] files) {
		for (File file : files) {
			if (file.isDirectory()) {
				deleteAll(file.listFiles());
			}

			file.delete();
		}
	}

	public static abstract class FileComparator implements Comparator<File> {
		private boolean ascending = true;

		public void setAscending(boolean ascending) {
			this.ascending = ascending;
		}

		public boolean isAscending() {
			return (this.ascending);
		}

		public int compare(File file1, File file2) {
			return (this.ascending ? this.compare(file1, file2) : -this.compare(file1, file2));
		}
	}

	private static class FileDateComparator extends FileComparator {
		public int compare(File file1, File file2) {
			return (new Date(file1.lastModified()).compareTo(new Date(file2.lastModified())));
		}
	}

	private static class FileNameComparator extends FileComparator {
		public int compare(File file1, File file2) {
			return file1.getName().compareTo(file2.getName());
		}
	}

	public static List<String> fromFile(String fileName) throws IOException {
		return fromFile(new File(fileName));
	}

	public static List<String> fromFile(File file) throws IOException {
		List<String> list = new ArrayList<String>();

		BufferedReader br = new BufferedReader(new FileReader(file));

		String s = "";

		while ((s = br.readLine()) != null) {
			list.add(s);
		}

		br.close();

		return list;
	}

	public static long getFileSize(File file) {
		if (!file.exists()) {
			return -1L;
		}
		long size = 0;
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			if (files != null && files.length > 0) {
				for (File f : files) {
					size += getFileSize(f);
				}
			}
		} else {
			size += file.length();
		}
		return size;
	}

	/**
	 * Finds files within a given directory (and optionally its
	 * subdirectories). All files found are filtered by an IOFileFilter.
	 */
	public static List<File> listFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
		//if(!directory.isDirectory()) {
		//    throw new IllegalArgumentException("Parameter 'directory' is not a directory");
		//}
		if (fileFilter == null) {
			throw new NullPointerException("Parameter 'fileFilter' is null");
		}
		//Setup effective file filter
		IOFileFilter effFileFilter = new AndFileFilter(fileFilter, new NotFileFilter(DirectoryFileFilter.INSTANCE));
		//Setup effective directory filter
		final IOFileFilter effDirFilter;
		if (dirFilter == null) {
			effDirFilter = FalseFileFilter.INSTANCE;
		} else {
			effDirFilter = new AndFileFilter(dirFilter, DirectoryFileFilter.INSTANCE);
		}
		//Find files
		List<File> files = new ArrayList<File>(12);
		innerListFiles(files, directory, new OrFileFilter(effFileFilter, effDirFilter));
		return files;
	}

	/**
	 * Finds files within a given directory (and optionally its
	 * subdirectories). All files found are filtered by an IOFileFilter.
	 */
	private static void innerListFiles(Collection<File> files, File directory, IOFileFilter filter) {
		File[] found = directory.listFiles((FileFilter) filter);
		if (found != null) {
			for (int i = 0; i < found.length; i++) {
				if (found[i].isDirectory()) {
					innerListFiles(files, found[i], filter);
				} else {
					files.add(found[i]);
				}
			}
		}
	}

	public static List<File> listFiles(File directory, String suffix, boolean recursive) {
		return listFiles(directory, new String[] { suffix }, recursive);
	}

	/**
	 * Finds files within a given directory (and optionally its subdirectories)
	 * which match an array of suffixes.
	 */
	public static List<File> listFiles(File directory, String[] suffixes, boolean recursive) {
		final IOFileFilter filter;
		if (suffixes == null) {
			filter = TrueFileFilter.INSTANCE;
		} else {
			filter = new SuffixFileFilter(suffixes);
		}
		return listFiles(directory, filter, (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
	}

	public static List<File> listFiles(File directory, String[] prefixes, String[] suffixes, boolean recursive) {
		IOFileFilter fileFiler = null;
		if (prefixes != null && prefixes.length > 0) {
			fileFiler = new PrefixFileFilter(prefixes);
		}
		if (suffixes != null && suffixes.length > 0) {
			fileFiler = new AndFileFilter(fileFiler, new SuffixFileFilter(suffixes));
		}
		return listFiles(directory, (fileFiler == null ? TrueFileFilter.INSTANCE : fileFiler),
				(recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
	}

	public static String getFileName(File file) {
		assert (file != null);
		if (!file.exists()) {
			return null;
		}
		String filepath = file.getName();
		int i = filepath.lastIndexOf(File.separator);
		return (i >= 0) ? filepath.substring(i + 1) : filepath;
	}

	public static String basename(String filepath) {
		final int index = filepath.lastIndexOf(File.separatorChar);
		if (-1 == index) {
			return filepath;
		} else {
			return filepath.substring(index + 1);
		}
	}

	public static String basename(String filepath, char separator) {
		final int index = filepath.lastIndexOf(separator);
		if (-1 == index) {
			return filepath;
		} else {
			return filepath.substring(index + 1);
		}
	}

	public static String dirName(String filepath, char separatorChar) {
		final int index = filepath.lastIndexOf(separatorChar);
		if (-1 == index) {
			return new String(new char[] { separatorChar });
		} else {
			return filepath.substring(0, index);
		}
	}

	public static void truncateFile(File file) {
		final RandomAccessFile raf;
		try {
			raf = new RandomAccessFile(file, "rw");
		} catch (FileNotFoundException fnfe) {
			throw new IllegalStateException(fnfe);
		}
		try {
			raf.setLength(0);
		} catch (IOException ioe) {
			throw new IllegalStateException(ioe);
		} finally {
			IOUtils.closeQuietly(raf);
		}
	}

	public static File toFile(URL url) {
		if (!url.getProtocol().equals("file")) {
			return null;
		} else {
			String filename = url.getFile().replace('/', File.separatorChar);
			return new File(filename);
		}
	}

	public interface IOFileFilter extends FileFilter, FilenameFilter {
	}

	public static final class TrueFileFilter implements IOFileFilter {
		static final TrueFileFilter INSTANCE = new TrueFileFilter();

		private TrueFileFilter() {
		}

		public boolean accept(File pathname) {
			return true;
		}

		public boolean accept(File dir, String name) {
			return true;
		}
	}

	public static final class FalseFileFilter implements IOFileFilter {
		static final FalseFileFilter INSTANCE = new FalseFileFilter();

		private FalseFileFilter() {
		}

		public boolean accept(File pathname) {
			return false;
		}

		public boolean accept(File dir, String name) {
			return false;
		}
	}

	public static final class PrefixFileFilter implements IOFileFilter {
		private final String[] prefixes;

		public PrefixFileFilter(String... prefixes) {
			if (prefixes == null) {
				throw new IllegalArgumentException("The array of prefixes must not be null");
			}
			this.prefixes = prefixes;
		}

		public boolean accept(File file) {
			String name = file.getName();
			for (int i = 0; i < this.prefixes.length; i++) {
				if (name.startsWith(this.prefixes[i])) {
					return true;
				}
			}
			return false;
		}

		public boolean accept(File file, String name) {
			for (int i = 0; i < prefixes.length; i++) {
				if (name.startsWith(prefixes[i])) {
					return true;
				}
			}
			return false;
		}
	}

	public static final class SuffixFileFilter implements IOFileFilter {
		private final String[] suffixes;

		public SuffixFileFilter(String... suffixes) {
			if (suffixes == null) {
				throw new IllegalArgumentException("The array of suffixes must not be null");
			}
			this.suffixes = suffixes;
		}

		public boolean accept(File file) {
			String name = file.getName();
			for (int i = 0; i < this.suffixes.length; i++) {
				if (name.endsWith(this.suffixes[i])) {
					return true;
				}
			}
			return false;
		}

		public boolean accept(File file, String name) {
			for (int i = 0; i < this.suffixes.length; i++) {
				if (name.endsWith(this.suffixes[i])) {
					return true;
				}
			}
			return false;
		}
	}

	public static final class AndFileFilter implements IOFileFilter {
		private final IOFileFilter[] fileFilters;

		public AndFileFilter(IOFileFilter... filter) {
			assert (filter != null);
			this.fileFilters = filter;
		}

		public boolean accept(final File file) {
			if (this.fileFilters.length == 0) {
				return false;
			}
			for (IOFileFilter fileFilter : fileFilters) {
				if (!fileFilter.accept(file)) {
					return false;
				}
			}
			return true;
		}

		public boolean accept(final File file, final String name) {
			if (this.fileFilters.length == 0) {
				return false;
			}
			for (IOFileFilter fileFilter : fileFilters) {
				if (!fileFilter.accept(file, name)) {
					return false;
				}
			}
			return true;
		}
	}

	public static final class OrFileFilter implements IOFileFilter {
		private final IOFileFilter[] fileFilters;

		public OrFileFilter(IOFileFilter... filter) {
			assert (filter != null);
			this.fileFilters = filter;
		}

		public boolean accept(final File file) {
			for (IOFileFilter fileFilter : fileFilters) {
				if (fileFilter.accept(file)) {
					return true;
				}
			}
			return false;
		}

		public boolean accept(final File file, final String name) {
			for (IOFileFilter fileFilter : fileFilters) {
				if (fileFilter.accept(file, name)) {
					return true;
				}
			}
			return false;
		}
	}

	public static final class NotFileFilter implements IOFileFilter {
		private final IOFileFilter filter;

		public NotFileFilter(IOFileFilter filter) {
			if (filter == null) {
				throw new IllegalArgumentException("The filter must not be null");
			}
			this.filter = filter;
		}

		public boolean accept(File file) {
			return !filter.accept(file);
		}

		public boolean accept(File file, String name) {
			return !filter.accept(file, name);
		}
	}

	public static final class DirectoryFileFilter implements IOFileFilter {
		public static final DirectoryFileFilter INSTANCE = new DirectoryFileFilter();

		private DirectoryFileFilter() {
		}

		public boolean accept(File file) {
			return file.isDirectory();
		}

		public boolean accept(File dir, String name) {
			return accept(new File(dir, name));
		}
	}

	public static final class NameFileFilter implements IOFileFilter {
		private final String[] names;

		public NameFileFilter(String... names) {
			if (names == null) {
				throw new IllegalArgumentException("The array of names must not be null");
			}
			this.names = names;
		}

		public boolean accept(File file) {
			String name = file.getName();
			for (int i = 0; i < this.names.length; i++) {
				if (name.equals(this.names[i])) {
					return true;
				}
			}
			return false;
		}

		public boolean accept(File file, String name) {
			for (int i = 0; i < this.names.length; i++) {
				if (name.equals(this.names[i])) {
					return true;
				}
			}
			return false;
		}
	}

	/**
	 * 将输入流里面的内容保存到本地文件系统
	 *
	 * @param in
	 * @param path
	 * @throws IOException
	 */
	public static void saveFile(InputStream in, String path) throws IOException {
		OutputStream out = null;
		try {
			File file = new File(path);
			File parent = file.getParentFile();
			if (!parent.exists())
				parent.mkdirs();
			file.createNewFile();
			out = new FileOutputStream(file);
			int length = (int) (in.available() > 4 * ONE_KB ? 4 * ONE_KB : in.available());
			byte[] bytes = new byte[length];
			int readNum = in.read(bytes, 0, length);
			while (readNum != -1) {
				out.write(bytes, 0, readNum);
				out.flush();
				readNum = in.read(bytes, 0, length);
			}
			bytes = null;
		} catch (Exception e) {
			throw new IOException(e.getMessage());
		} finally {
			IOUtils.closeQuietly(in, out);
		}
	}

	public static void saveFile(byte[] bytes, String path) throws IOException {
		File file = new File(path);
		File parent = file.getParentFile();
		if (!parent.exists())
			parent.mkdirs();
		file.createNewFile();
		FileOutputStream out = new FileOutputStream(file);
		out.write(bytes);
		bytes = null;
		out.close();
	}

	/**
	 * 返回一个文件的文件名,去掉文件后缀名和父目录

	 *
	 *
	 *
	 * @param filename
	 * @return
	 */
/*	public static String getFileName(String name) {
		String filename = normalizePath(name, '/');
		int pos2 = filename.lastIndexOf('/');
		if (pos2 != -1)
			filename = filename.substring(pos2 + 1);
		int pos = filename.lastIndexOf('.');
		if (pos == -1)
			return filename;
		return filename.substring(0, pos);
	}*/
	 /**
	   * Gets the file name from a path using the system's separator character
	   * @param filePath The file path
	   * @return The file name
	   */
	  public static String getFileName(String filePath) {
	    int separatorIndex = filePath.lastIndexOf(File.separatorChar);
	    if (separatorIndex == -1) {
	      return filePath;
	    }
	    else {
	      return filePath.substring(separatorIndex + 1);
	    }
	  }

	/**
	 * 返回一个文件的后缀 如.txt,.doc
	 *
	 * @param filename
	 * @return
	 */
	public static String getPostfix(String filename) {
		int pos = filename.lastIndexOf('.');
		if (pos == -1)
			return "";
		return filename.substring(pos);
	}

	/**
	 * 将一个文件的名称转为规范的形式

	 *
	 *
	 *
	 * @param path
	 * @return
	 */
	public static String normalizePath(String path) {

		return normalizePath(path, File.separatorChar);
	}

	/**
	 * 将一个文件的名称根据传入的文件分隔符转化为规范的形式
	 *
	 * @param path
	 * @param separatorChar
	 * @return
	 */
	public static String normalizePath(String p, char separatorChar) {
		String path = p;

		if (StringUtils.isNotEmpty(path)) {
			// ensure all File separators are '/'
			path = path.replace('\\', '/');
			String drive = null;
			if ((path.length() > 1) && (path.charAt(1) == ':')) {
				// windows path like C:\home\
				drive = path.substring(0, 2);
				path = path.substring(2);
			}
			if (path.charAt(0) == '/') {
				// trick to resolve all ../ inside a path
				path = '.' + path;
			}
			// resolve all '../' or './' elements in the path
			path = getAbsoluteUri(path, "/");
			// re-append drive if required
			if (drive != null) {
				path = drive.concat(path);
			}
			// still some '//' elements might persist
			path = StringUtils.substitute(path, "//", "/");
			// switch '/' back to OS dependend File separator if required
			if (separatorChar != '/') {
				path = path.replace('/', separatorChar);
			}
		}
		return path;
	}

	/**
	 * 返回一个相应路径的绝对路径
	 *
	 * @param relativeUri
	 * @param baseUri
	 * @return
	 */
	public static String getAbsoluteUri(String relativeUri, String baseUri) {

		if ((relativeUri == null) || (relativeUri.length() >= 1 && relativeUri.charAt(0) == '/')) {
			// uri is null or already absolute
			return relativeUri;
		}
		try {
			URL baseUrl = new URL("http://127.0.0.1");
			URL url = new URL(new URL(baseUrl, baseUri), relativeUri);
			if (url.getQuery() == null) {
				return url.getPath();
			} else {
				StringBuffer result = new StringBuffer(url.getPath().length() + url.getQuery().length() + 2);
				result.append(url.getPath());
				result.append('?');
				result.append(url.getQuery());
				return result.toString();
			}
		} catch (MalformedURLException e) {
			log.error(e.getMessage(), e);
			return relativeUri;
		}
	}

	/**
	* 将文件中的内容写入OutputSteam中，并返回实际写入的字节数。
	* @param input
	* @param output
	* @return
	* @throws IOException
	*/
	public static long write(File input, OutputStream output) throws IOException {
		InputStream in = null;
		try {
			in = openInputStream(input);
			return write(in, output);
		} finally {
			IOUtils.close(in);
		}
	}

	/**
	 * 将InputStream中的内容写入文件中，并返回实际写入的字节数。
	 * @param input
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static long write(InputStream input, File file) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			return write(input, out);
		} finally {
			IOUtils.close(out);
		}
	}

	/**
	 * 将String写入指定文件。
	 *
	 * @param data
	 * @param file
	 * @throws IOException
	 */
	public static void write(String data, File file) throws IOException {
		write(data, file, null);
	}

	/**
	 * 将byte数组写入OutputStream 。
	 *
	 * @param data
	 * @param output
	 * @throws IOException
	 */
	public static void write(byte[] data, OutputStream output) throws IOException {
		if (data != null) {
			output.write(data);
		}
	}

	/**
	 * 将byte数组写入文件。
	 *
	 * @param data
	 * @param file
	 * @throws IOException
	 */
	public static void write(byte[] data, File file) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			write(data, out);
		} finally {
			IOUtils.close(out);
		}
	}

	/**
	 * 将String按指定编码格式写入OutputStream 。
	 *
	 * @param data
	 * @param output
	 * @param encoding
	 * @throws IOException
	 */
	public static void write(String data, OutputStream output, String encoding) throws IOException {
		if (data != null) {
			if (encoding == null) {
				write(data, output);
			} else {
				output.write(data.getBytes(encoding));
			}
		}
	}

	/**
	 * 将String按指定编码格式写入文件。
	 *
	 * @param data
	 * @param file
	 * @param encoding
	 * @throws IOException
	 */
	public static void write(String data, File file, String encoding) throws IOException {
		OutputStream out = null;
		try {
			out = openOutputStream(file);
			write(data, out, encoding);
		} finally {
			IOUtils.close(out);
		}
	}

	/**
	 * 将String写入OutputStream 。
	 *
	 * @param data
	 * @param output
	 * @throws IOException
	 */
	public static void write(String data, OutputStream output) throws IOException {
		if (data != null) {
			output.write(data.getBytes());
		}
	}

	/**
	 * 将InputStream中的内容写入OuputStream中，并返回实际写入的字节数。
	 *
	 * @param input
	 * @param output
	 * @return
	 * @throws IOException
	 */
	public static long write(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[1024 * 4];
		long count = 0;
		int n = 0;
		while (-1 != (n = input.read(buffer))) {
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}
	  /**
     * 清除目录下的子目录和文件
     *
     * @param directory
     * @throws IOException
     */
    public static void cleanDirectory(File directory) throws IOException {
            if (directory == null || !directory.exists()
                            || !directory.isDirectory())
                    return;

            File[] files = directory.listFiles();
            if (files == null) { // null if security restricted
                    throw new IOException("Failed to list contents of " + directory);
            }

            IOException exception = null;
            for (int i = 0; i < files.length; i++) {
                    File file = files[i];
                    try {
                            forceDelete(file);
                    } catch (IOException ioe) {
                            exception = ioe;
                    }
            }

            if (null != exception) {
                    throw exception;
            }

    }

  //---------------------------------------------------------------------
	// Copy methods for java.io.File
	//---------------------------------------------------------------------

	/**
	 * Copy the contents of the given input File to the given output File.
	 * @param in the file to copy from
	 * @param out the file to copy to
	 * @return the number of bytes copied
	 * @throws IOException in case of I/O errors
	 */
	public static int copy(File in, File out) throws IOException {
		Assert.notNull(in, "No input File specified");
		Assert.notNull(out, "No output File specified");
		return copy(new BufferedInputStream(new FileInputStream(in)),
		    new BufferedOutputStream(new FileOutputStream(out)));
		 
	}

	/**
	 * Copy the contents of the given byte array to the given output File.
	 * @param in the byte array to copy from
	 * @param out the file to copy to
	 * @throws IOException in case of I/O errors
	 */
	public static void copy(byte[] in, File out) throws IOException {
		Assert.notNull(in, "No input byte array specified");
		Assert.notNull(out, "No output File specified");
		ByteArrayInputStream inStream = new ByteArrayInputStream(in);
		OutputStream outStream = new BufferedOutputStream(new FileOutputStream(out));
		copy(inStream, outStream);
	}

	/**
	 * Copy the contents of the given input File into a new byte array.
	 * @param in the file to copy from
	 * @return the new byte array that has been copied to
	 * @throws IOException in case of I/O errors
	 */
	public static byte[] copyToByteArray(File in) throws IOException {
		Assert.notNull(in, "No input File specified");
		return copyToByteArray(new BufferedInputStream(new FileInputStream(in)));
	}


	//---------------------------------------------------------------------
	// Copy methods for java.io.InputStream / java.io.OutputStream
	//---------------------------------------------------------------------


	/**
	 * Copy the contents of the given byte array to the given OutputStream.
	 * Closes the stream when done.
	 * @param in the byte array to copy from
	 * @param out the OutputStream to copy to
	 * @throws IOException in case of I/O errors
	 */
	public static void copy(byte[] in, OutputStream out) throws IOException {
		Assert.notNull(in, "No input byte array specified");
		Assert.notNull(out, "No OutputStream specified");
		try {
			out.write(in);
		}
		finally {
			try {
				out.close();
			}
			catch (IOException ex) {
				log.warn("Could not close OutputStream", ex);
			}
		}
	}

	/**
	 * Copy the contents of the given InputStream into a new byte array.
	 * Closes the stream when done.
	 * @param in the stream to copy from
	 * @return the new byte array that has been copied to
	 * @throws IOException in case of I/O errors
	 */
	public static byte[] copyToByteArray(InputStream in) throws IOException {
		ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
		copy(in, out);
		return out.toByteArray();
	}


	//---------------------------------------------------------------------
	// Copy methods for java.io.Reader / java.io.Writer
	//---------------------------------------------------------------------

	/**
	 * Copy the contents of the given Reader to the given Writer.
	 * Closes both when done.
	 * @param in the Reader to copy from
	 * @param out the Writer to copy to
	 * @return the number of characters copied
	 * @throws IOException in case of I/O errors
	 */
	public static int copy(Reader in, Writer out) throws IOException {
		Assert.notNull(in, "No Reader specified");
		Assert.notNull(out, "No Writer specified");
		try {
			int byteCount = 0;
			char[] buffer = new char[BUFFER_SIZE];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		}
		finally {
			try {
				in.close();
			}
			catch (IOException ex) {
				log.warn("Could not close Reader", ex);
			}
			try {
				out.close();
			}
			catch (IOException ex) {
				log.warn("Could not close Writer", ex);
			}
		}
	}

	/**
	 * Copy the contents of the given String to the given output Writer.
	 * Closes the write when done.
	 * @param in the String to copy from
	 * @param out the Writer to copy to
	 * @throws IOException in case of I/O errors
	 */
	public static void copy(String in, Writer out) throws IOException {
		Assert.notNull(in, "No input String specified");
		Assert.notNull(out, "No Writer specified");
		try {
			out.write(in);
		}
		finally {
			try {
				out.close();
			}
			catch (IOException ex) {
				log.warn("Could not close Writer", ex);
			}
		}
	}

	/**
	 * Copy the contents of the given Reader into a String.
	 * Closes the reader when done.
	 * @param in the reader to copy from
	 * @return the String that has been copied to
	 * @throws IOException in case of I/O errors
	 */
	public static String copyToString(Reader in) throws IOException {
		StringWriter out = new StringWriter();
		copy(in, out);
		return out.toString();
	}

    /** True if file is a JAR or ZIP file */
    public static boolean isArchive(String fileName)
    {
            if (fileName == null)
                    return false;
            return fileName.endsWith(".jar"); // || fileName.endsWith(".zip")));
    }

    /**
 * Checks if the input is a class file
 *
 * @param cf the input to check.
 * @return
 */
    public static boolean isClassFile(String cf)
    {
            if (cf == null)
                    return false;
            else
                    return ((cf.endsWith(".class") && (cf.indexOf("$") == -1)));
    }

    public static boolean isInnerClassFile(String cf)
    {
            if (cf == null)
                    return false;
            else
                    return ((cf.endsWith(".class") && (cf.indexOf("$") != -1)));
    }
    /**
     * Accepts valid directory, picks up all JAR, .class and ZIP files
     *
     * @param dirName if valid dir. will process, else will do nothing
     * @param recursive when true will process directory tree recursively
     */
        public static Set<File> getFiles(String dirName, boolean recursive) throws IOException
        {
                Set<File> fileList = new HashSet<File>();
                File dir = getFileHandle(dirName);
                if (!dir.isDirectory())
                        return fileList;// return an empty list

                // The file is directory, Check for files recursively
                File[] files = dir.listFiles();
                for (File f : files)
                {
                        if (f.isDirectory() && f.canRead() && recursive)
                        {
                                fileList.addAll(getFiles(f.getPath(), recursive)); // recursive-call
                        } else
                        {
                                fileList.add(f);
                        }
                }
                return fileList;
        }

        /** Returns File is it exists, and has read permission -- exception if not */
        public static File getFileHandle(String fileName) throws IOException
        {
                File f = new File(fileName.trim());
                if (!f.canRead())
                        throw new FileNotFoundException(fileName);
                return f;
        }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		System.out.println(byteCountToDisplaySize(-667048));
	}
	public static class InnerFileFilterIterator extends FileIterator {

		private FileFilter filter;

		public InnerFileFilterIterator(File file, FileFilter filter) {
			super(file);
			this.filter = filter;
		}
		@Override
		public boolean hasNext() {
			if (super.hasNext()) {
				if (this.filter.accept(super.next())) {
					return true;
				} else {
					return this.hasNext();
				}
			} else {
				return false;
			}
		}
	}

	private static class InnerFilenameFilterIterator extends FileIterator {

		private FilenameFilter filter;

		private File directory;

		public InnerFilenameFilterIterator(File file, FilenameFilter filter) {
			super(file);
			this.directory = file;
			this.filter = filter;
		}

		public boolean hasNext() {
			if (super.hasNext()) {
				if (this.filter.accept(this.directory, this.next().getName())) {
					return true;
				} else {
					return this.hasNext();
				}
			} else {
				return false;
			}
		}
	}
	   /**
     * Load a text file in CharSequence
     *
     * @param file
     *            The file to load
     * @return The text content of the file as {@link CharSequence}
     * @throws FileNotFoundException
     * @throws IOException
     */
    static public CharSequence load(File source, String charsetName)
                    throws FileNotFoundException, IOException {
            StringBuilder contents = new StringBuilder();

            Scanner scanner = new Scanner(source, charsetName);

            try {
                    while (scanner.hasNextLine()) {
                            String line = scanner.nextLine();
                            contents.append(line);
                            if (scanner.hasNextLine()) {
                                    contents.append("\n");
                            }
                    }
            } finally {
                    scanner.close();
            }

            return contents;

    }

	/**
	 * Returns a FileIterator object, used to traverse the contents of the directory  
	 * denoted by the File argument. Only the files that satisfy the given filter will 
	 * appear in this FileIterator and they'll not be in any particular order. 
	 * 
	 * @param file the directory to be listed.
	 * @param filter the filter to aply to the contents of the directory.
	 * @return a FileIterator object.
	 */
	public static FileIterator listFiles(File file, FileFilter filter) {
		return new InnerFileFilterIterator(file, filter);
	}

	/**
	 * Returns a FileIterator object, used to traverse the contents of the directory  
	 * denoted by the File argument. Only the files that satisfy the given filter will 
	 * appear in this FileIterator and they'll not be in any particular order. 
	 * 
	 * @param file the directory to be listed.
	 * @param filter the filter to aply to the contents of the directory.
	 * @return a FileIterator object.
	 */
	public static FileIterator listFiles(File file, FilenameFilter filter) {
		return new InnerFilenameFilterIterator(file, filter);
	}

}
